一、jsx变createElement
每一个用jsx语法书写的react组件最后都会变成 react.createElement(...)这一坨东西,
// 转变前 export default (props)=>( <h1 ref="h1" key="header1" name="我"><span>哈哈!</span>我是header.{props.kk}</h1> );
// 转变后 var _default = function _default(props) { return _react2.default.createElement( "h1", { ref: "h1", key: "header1", name: "u6211" }, _react2.default.createElement( "span", null, "u54C8u54C8uFF01" ), "u6211u662Fheader.", props.kk ); };
通过看代码就知道:header这个组件有三个子元素: span text 变量
可以说每遇到一个html标签就用createElement做包装,text 和 变量 不包装,直接按顺序当做参数传入createElement,有多少传多少
二、createElement拿到这些参数都干了啥
撸到createElement的源码块所在文件:
// react中createElement方法来源于 ReactElement.js import { createElement, createFactory, cloneElement, isValidElement, } from './ReactElement';
找到createElement的源码:
/** * 传入了如下参数: * type: "h1" * config: { ref: "h1", key: "header1", name: "u6211" } * children: 1.react.createElement(...) * 2.'我是header' * 3. props.kk */ function createElement(type, config, children) { // 一堆变量初始化 let propName; const props = {}; let key = null; let ref = null; let self = null; let source = null; // 如果组件上存在属性设置,比如ref、key 其他props什么的 if (config != null) { // 判断是否有ref属性且ref属性有值 单独取出来 if (hasValidRef(config)) { ref = config.ref; } // 判断是否有key,单独取出来 if (hasValidKey(config)) { key = '' + config.key; } // 先不管self 跟 source是干什么用的 self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // 把剩余的属性塞到props里面 for (propName in config) { if ( /* const hasOwnProperty = Object.prototype.hasOwnProperty; const RESERVED_PROPS = { key: true, ref: true, __self: true, __source: true, }; */ // 严谨的判断config对象中是否存在改属性,且属性名不能是react保留的那四种 hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { // 放入props中 props[propName] = config[propName]; } } } // 处理后面那些children // 算出有几个children const childrenLength = arguments.length - 2; if (childrenLength === 1) { // 如果就一个 直接赋值 props.children = children; } else if (childrenLength > 1) { // 整一个childArray 保存那些children const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } // dev环境不管丫的 if (__DEV__) { .... } // 最终还是塞到props里面 props.children = childArray; } // 如果type传的东西是个对象,且type有defaultProps这个东西,那就defaultProps的值也塞props里面 if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } if (__DEV__) { ... //附加一堆开发环境才有的东西,先不去管它 } // 最后返回ReactElement 函数执行后的返回值 return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); }
我们注意到里面有一个 ReactCurrentOwner.current这个东西是个外来的,找到它:
const ReactCurrentOwner = { /** * @internal * @type {ReactComponent} */ current: (null: null | Fiber), currentDispatcher: (null: null | Dispatcher), }; // 实际上这个current初始时是null,类型可以是Fiber或null
其实绕来绕去,核心是 return ReactElement(...)这么一堆东西,就像剥洋葱,还得往下扒皮
三、ReactElement返回组件的真正形态
// 判断浏览器是否支持Symbol const hasSymbol = typeof Symbol === 'function' && Symbol.for; // 如果支持Symbol 则创建,否则用数字代替 export const REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7; const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { $$typeof: REACT_ELEMENT_TYPE, // Symbol('react.element'); type: type, // h1 key: key, // header1 ref: ref, // h1 props: props, // {name:'u6211',children:[...,...,...]} _owner: owner, // null }; if (__DEV__) { ... } return element; };
这个element打印出来,其实它就是一个简简单单的对象
其他:
Symbol.for('abc') 和 Symbol('abc')有什么区别呢?