内容简介:ReactElement 下面导出了多个方法:来看看一个示例:
ReactElement 下面导出了多个方法:
-
createElement
用于创建并返回一个新的ReactElement
元素 -
createFactory
用于返回固定类的createElement
方法 【已废弃】 -
cloneElement
克隆一个元素 -
isValidElement
验证一个对象是否为ReactElement
。 -
cloneAndReplaceKey
使用给定key
返回一个新的ReactElement
类型
源码
包:packages/react/src/ReactElement.js
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import invariant from 'shared/invariant'; import warningWithoutStack from 'shared/warningWithoutStack'; import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; import ReactCurrentOwner from './ReactCurrentOwner'; const hasOwnProperty = Object.prototype.hasOwnProperty; const RESERVED_PROPS = { key: true, ref: true, __self: true, __source: true, }; let specialPropKeyWarningShown, specialPropRefWarningShown; function hasValidRef(config) { if (__DEV__) { if (hasOwnProperty.call(config, 'ref')) { const getter = Object.getOwnPropertyDescriptor(config, 'ref').get; if (getter && getter.isReactWarning) { return false; } } } return config.ref !== undefined; } function hasValidKey(config) { if (__DEV__) { if (hasOwnProperty.call(config, 'key')) { const getter = Object.getOwnPropertyDescriptor(config, 'key').get; if (getter && getter.isReactWarning) { return false; } } } return config.key !== undefined; } function defineKeyPropWarningGetter(props, displayName) { const warnAboutAccessingKey = function() { if (!specialPropKeyWarningShown) { specialPropKeyWarningShown = true; warningWithoutStack( false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName, ); } }; warnAboutAccessingKey.isReactWarning = true; Object.defineProperty(props, 'key', { get: warnAboutAccessingKey, configurable: true, }); } function defineRefPropWarningGetter(props, displayName) { const warnAboutAccessingRef = function() { if (!specialPropRefWarningShown) { specialPropRefWarningShown = true; warningWithoutStack( false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName, ); } }; warnAboutAccessingRef.isReactWarning = true; Object.defineProperty(props, 'ref', { get: warnAboutAccessingRef, configurable: true, }); } /** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, no instanceof check * will work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} key * @param {string|object} ref * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @param {*} owner * @param {*} props * @internal */ const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }; if (__DEV__) { // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. element._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should // include every environment we run tests in), so the test framework // ignores it. Object.defineProperty(element._store, 'validated', { configurable: false, enumerable: false, writable: true, value: false, }); // self and source are DEV only properties. Object.defineProperty(element, '_self', { configurable: false, enumerable: false, writable: false, value: self, }); // Two elements created in two different places should be considered // equal for testing purposes and therefore we hide it from enumeration. Object.defineProperty(element, '_source', { configurable: false, enumerable: false, writable: false, value: source, }); if (Object.freeze) { Object.freeze(element.props); Object.freeze(element); } } return element; }; /** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement */ export function createElement(type, config, children) { let propName; // Reserved names are extracted const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // Resolve default props if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } if (__DEV__) { if (key || ref) { const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); } /** * Return a function that produces ReactElements of a given type. * See https://reactjs.org/docs/react-api.html#createfactory */ export function createFactory(type) { const factory = createElement.bind(null, type); // Expose the type on the factory and the prototype so that it can be // easily accessed on elements. E.g. `<Foo />.type === Foo`. // This should not be named `constructor` since this may not be the function // that created the element, and it may not even be a constructor. // Legacy hook: remove it factory.type = type; return factory; } export function cloneAndReplaceKey(oldElement, newKey) { const newElement = ReactElement( oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props, ); return newElement; } /** * Clone and return a new ReactElement using element as the starting point. * See https://reactjs.org/docs/react-api.html#cloneelement */ export function cloneElement(element, config, children) { invariant( !(element === null || element === undefined), 'React.cloneElement(...): The argument must be a React element, but you passed %s.', element, ); let propName; // Original props are copied const props = Object.assign({}, element.props); // Reserved names are extracted let key = element.key; let ref = element.ref; // Self is preserved since the owner is preserved. const self = element._self; // Source is preserved since cloneElement is unlikely to be targeted by a // transpiler, and the original source is probably a better indicator of the // true owner. const source = element._source; // Owner will be preserved, unless ref is overridden let owner = element._owner; if (config != null) { if (hasValidRef(config)) { // Silently steal the ref from the parent. ref = config.ref; owner = ReactCurrentOwner.current; } if (hasValidKey(config)) { key = '' + config.key; } // Remaining properties override existing props let defaultProps; if (element.type && element.type.defaultProps) { defaultProps = element.type.defaultProps; } for (propName in config) { if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { if (config[propName] === undefined && defaultProps !== undefined) { // Resolve default props props[propName] = defaultProps[propName]; } else { props[propName] = config[propName]; } } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } props.children = childArray; } return ReactElement(element.type, key, ref, self, source, owner, props); } /** * Verifies the object is a ReactElement. * See https://reactjs.org/docs/react-api.html#isvalidelement * @param {?object} object * @return {boolean} True if `object` is a ReactElement. * @final */ export function isValidElement(object) { return ( typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE ); } 复制代码
ReactElement
/** * * 用来创建 React 元素的工厂方法。 * 不再遵循类的模式,因为不能使用 new 来调用它,instance 检查无效。 * 取而代之,可以检测 $$typeof 字段与 Symbol.for('react.element') 是否匹配来判断是否是一个 React 元素 * * @param {*} type * @param {*} key * @param {string|object} ref * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @param {*} owner * @param {*} props * @internal */ const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // 这个标记允许我们唯一地将其标识为 React 元素 $$typeof: REACT_ELEMENT_TYPE, // 元素的内置属性 type: type, key: key, ref: ref, props: props, // 记录负责创建此元素的组件. _owner: owner, }; if (__DEV__) { // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. element._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should // include every environment we run tests in), so the test framework // ignores it. Object.defineProperty(element._store, 'validated', { configurable: false, enumerable: false, writable: true, value: false, }); // self and source are DEV only properties. Object.defineProperty(element, '_self', { configurable: false, enumerable: false, writable: false, value: self, }); // Two elements created in two different places should be considered // equal for testing purposes and therefore we hide it from enumeration. Object.defineProperty(element, '_source', { configurable: false, enumerable: false, writable: false, value: source, }); if (Object.freeze) { Object.freeze(element.props); Object.freeze(element); } } return element; };复制代码
createElement
React.createElement
函数在 React 中具有举足轻重的地位,我们的组件最后都会编译成它。
/** * 使用给定 type 创建并返回的新的 React 元素。 * See https://reactjs.org/docs/react-api.html#createelement */ export function createElement(type, config, children) { let propName; // Reserved names are extracted const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // 剩余的属性被添加到一个新的 props 对象中 for (propName in config) { if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; } } } // Children 可以是多个参数,这些参数被转移到新分配的 props 对象上。 const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // 解析默认 props if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } if (__DEV__) { if (key || ref) { const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); }复制代码
来看看一个示例:
React.createElement('div');复制代码
返回一个对象,对象如下:
可以看到 就是创建了一个普通的 javascript 对象。这时候,产生了几个疑问:
- 这个普普通通的 javascript 对象是如何变成了我们页面的 DOM 结构的
- DOM 层级如此之多,信息之复杂,React 又是如何实现的?
- 上一章节中讲到的
Component
,它经过编译后传递给了React.createElement
方法什么样的参数,类中的实例属性和原型方法如何进行了传递。
isValidElement
通过判断对象的 $$typeof
属性与 Symbol.for('react.element')
是否相同来,验证一个对象是否为 React 元素。
通过 React.createElement
方法创建的对象,都含有一个值完全相同的 $$typeof
属性,标识其为一个 React 元素。
export function isValidElement(object) { return ( typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE ); }复制代码
React 如何实现 DOM 层级
来看下面的 Hello
组件:
function Hello(props) { return ( <div> Hello { props.name } <p className="title">React</p> <p className="content">React is cool!</p> <MyFooter className="footer"> <div className="concat">concat</div> <div className="info">company info</div> </MyFooter> </div> ); }复制代码
MyFooter
组件:
function MyFooter(props) { return ( <div className="footer"> { props.children } </div> ); } 复制代码
我们采用了函数式组件的方式,他们将分别被编译为:
// Hello 组件 function Hello(props) { return React.createElement("div", null, "Hello ", props.name, React.createElement("p", { className: "title" }, "React"), React.createElement("p", { className: "content" }, "React is cool!"), React.createElement(MyFooter, { className: "footer" }, React.createElement("div", { className: "concat" }, "concat"), React.createElement("div", { className: "info" }, "company info")) ); } // MyFooter 组件 function MyFooter(props) { return React.createElement("div", { className: "footer" }, props.children); }复制代码
首先,从上面的代码我们可以看出下面几点:
- 组件都会被编译成
React.createElement,
不论是自定义组件还是原始的 html 标签,都会被编译器编译。 -
React.createElement
方法的参数个数是可变的,在上面源码分析中,我们已经看到从第三个参数开始的所有参数会打包为一个数组,存入 React 元素的props
属性的children
中。 - 不论从组件的哪一级部分开始划分,其子元素都是通过函数参数传递进父级的,并最后都会存放于
props
属性的children
中。 - 自定义组件的
type
参数值是组件的直接引用,而不是组件名的字符串。MyFooter 组件的 type 属性是一个函数,是它本身,而不是"MyFooter"
字符串。
再看看生成的 JavaScript 对象:
所谓的层级结构就是通过 React 元素的 props
属性中的 children
属性层层嵌套得来的。
遗留问题
- 普通的 javascript 对象(React 元素)是如何变成了我们页面的 DOM 结构的
-
Component
经过编译后传递给了React.createElement
方法什么样的参数,类中的实例属性和原型方法如何进行了传递。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 直观理解深度学习基本概念(小白入门深度学习)
- 深度学习的关键:无监督深度学习简介(附Python代码)
- 微软收购深度学习初创公司Lobe 以帮助创建深度学习模型
- 人工智能深度学习Caffe框架介绍,优秀的深度学习架构
- Python猫荐书系列:文也深度学习,理也深度学习
- 【ENVI深度学习】使用ENVI深度学习工具快速识别蔬菜大棚
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。