内容简介: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深度学习工具快速识别蔬菜大棚
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
两周自制脚本语言
[日]千叶 滋 / 陈筱烟 / 人民邮电出版社 / 2014-6 / 59.00元
《两周自制脚本语言》是一本优秀的编译原理入门读物。全书穿插了大量轻松风趣的对话,读者可以随书中的人物一起从最简单的语言解释器开始,逐步添加新功能,最终完成一个支持函数、数组、对象等高级功能的语言编译器。本书与众不同的实现方式不仅大幅简化了语言处理器的复杂度,还有助于拓展读者的视野。 《两周自制脚本语言》适合对编译原理及语言处理器设计有兴趣的读者以及正在学习相关课程的大中专院校学生。同时,已经......一起来看看 《两周自制脚本语言》 这本书的介绍吧!