1、JSX语法转换到Js语法
从 JSX 转换到 JS 会用到React.createElement()
,因此先熟悉下 JSX 到 JS 的转换。html
这边是 JSX 语法:react
<div id='one' class='two'> <span id="spanOne">this is spanOne</span> <span id="spanTwo">this is spanTwo</span> </div>
这边是转化成的 js 语法:git
React.createElement( "div", { id: "one", class: "two" }, React.createElement( "span", { id: "spanOne" }, "this is spanOne"), React.createElement("span", { id: "spanTwo" }, "this is spanTwo") );
React.createElement("标签名","Object,包含div的props",'children子节点1','children子节点2','...')
es6
这边是 JSX 语法:github
function Div(){ } <Div id='one' class='two'> <span id="spanOne">this is spanOne</span> <span id="spanTwo">this is spanTwo</span> </Div>
这边是转化成的 js 语法:数组
React.createElement(Div, {} , xxx );
若是标签名大写,则表示组件 Div(也就是function
),小写表示 html 的标签 <div>
微信
也就是说:自定义的组件必须大写字母开头app
2、React.createElement()
源码地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.jside
做用:
建立React.Element
,示例请看1、JSX语法转换到Js语法
测试
源码:
//注意:react只写了3个参数,实际上,从第三个参数日后都是children 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; //赋给标签的props不为空时 if (config != null) { if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { //防止是Number key = '' + config.key; } //__self、__source 暂时不知道是干啥用的属性 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) { //若是config中的属性不是标签原生属性,则放入props对象中 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); //依次将children push进array中 for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } //若是是development环境的话 if (__DEV__) { //冻结array //未在微信发表 //https://www.jianshu.com/p/91e5dc520c0d?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends&from=singlemessage&isappinstalled=0 if (Object.freeze) { Object.freeze(childArray); } } //开发中写的this.props.children就是子元素的集合 props.children = childArray; } // Resolve default props //为传入的props设置默认值,好比: //class Comp extends React.Component{ // static defaultProps = { // aaa: 'one', // bbb: () => {}, // ccc: {}, // }; // // } if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { //若是props数组中未设值,则设置默认值(注意:null也算设置了值) if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } if (__DEV__) { //一旦ref或key存在 if (key || ref) { //若是type是组件的话,赋值displayName const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; //可不看 if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement( type, //'div' key, //null ref, //null self, //null source, //null ReactCurrentOwner.current, //null或Fiber props, //自定义的属性、方法,注意:props.children=childArray ); }
解析:
(1)hasValidRef()
做用:
判断是否设置了ref的属性,true
有,false
没有
源码:
//判断是否设置了ref的属性,true有,false没有 function hasValidRef(config) { //若是是development环境的话 if (__DEV__) { //若是config中存在ref属性的话 //在jQuery中 .call/.apply的更大做用是绑定this if (hasOwnProperty.call(config, 'ref')) { //Object.getOwnPropertyDescriptor() es5 //Object.getOwnPropertyDescriptors() es6 //https://blog.csdn.net/qq_30100043/article/details/53424963 //返回对象config的属性ref 的get对象 const getter = Object.getOwnPropertyDescriptor(config, 'ref').get; //若是isReactWarning,则忽略ref属性,返回false if (getter && getter.isReactWarning) { return false; } } } //<div ref={this.optionsTEchart} ></div> return config.ref !== undefined; }
① 注意:__DEV__
表示测试环境,是供React
内部测试的,能够不看,我简单地解释了下
② 在jQuery
中fn.call(xxx,a1,a2,...)
或fn.apply(xxx,array)
的更大做用是绑定this
③ Object.getOwnPropertyDescriptor()
的做用是返回某个对象属性的描述对象( descriptor )
好比:
var obj = { p: 'a' }; Object.getOwnPropertyDescriptor(obj, 'p') //返回 // Object { value: "a", // writable: true, // enumerable: true, // configurable: true }
关于Object.getOwnPropertyDescriptor()
和Object.getOwnPropertyDescriptors()
的区别,请看:https://blog.csdn.net/qq_30100043/article/details/53424963
(2)hasValidKey
做用:
判断是否设置了key
,同hasValidRef
,不解释了
源码:
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; }
(3)虽然React.createElement()
只传三个参数,但从第三个参数开始,利用arguments
来获取剩下的参数
(4)Object.freeze()
使用Object.freeze()
冻结的对象是最严格的防篡改级别,既不可扩展,也是密封的,不可修改属性。
对于 JS 库做者而言,冻结对象可防止有人修改库的核心对象。
关于 JS 冻结对象的方法,请看:JS红皮书解读之防篡改对象
(5)最后是 return 了ReactElement()
方法,注意props
中的children
属性就是React
组件的children
react组件的children属性不会被覆盖:
父组件:
return( <DashBoard children={'bbbb'}> aaaa </DashBoard> )
子组件:
console.log(this.props)
结果:
3、ReactElement()
源码地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.js
做用:
经过工厂模式建立React.Element
对象,你打印一个React
组件的话,会是下面这个样子:
源码:
/** * 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 {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @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. * @internal */ // type, //'div' // key, //null // ref, //null // self, //null // source, //null // ReactCurrentOwner.current, //null或Fiber // props, //自定义的属性、方法,注意:props.children=childArray const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element //标识element的类型 //由于jsx都是经过createElement建立的,因此ReactElement的类型固定:为REACT_ELEMENT_TYPE //重要!由于react最终渲染到DOM上时,须要判断$$typeof===REACT_ELEMENT_TYPE $$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. //记录建立react.element的组件(this?) _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. //验证flag是不固定的.咱们将其放置在一个store上,从而能冻结整个object //这样一旦它们被用在开发环境时,用WeakMap代替 //WeakMap // http://es6.ruanyifeng.com/#docs/set-map 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; };
解析:
(1)经过$$typeof
确保是React.Element
类型,从而渲染到真正的DOM
树上
(2)__DEV__
注释中有提到WeakMap
,
简单说下WeakMap
的做用:
你往WeakMap
上的对象 a 添加数据,对象 b 引用 对象 a,以后对象 b 不引用 对象 a,a 就被垃圾回收,不用WeakMap
的话,即便对象 b 之后不引用对象 a了,a 也不会被垃圾回收,由于强引用是不会触发垃圾回收机制的,须要手动删除,很麻烦。
想更详细地了解的话,能够参考下这篇文章:
http://es6.ruanyifeng.com/#docs/set-map
关于垃圾回收机制,请看:浅谈下垃圾回收机制(1)
(3)该方法比较简单,就是初始化了一个对象,并将其标记为React.Element
对象($$typeof=REACT_ELEMENT_TYPE
)
(完)