一、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, "\u54C8\u54C8\uFF01" ), "\u6211\u662Fheader.", 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')有什么區別呢?