一、JSX语法转换到Js语法
从 JSX 转换到 JS 会用到React.createElement(),所以先熟悉下 JSX 到 JS 的转换。

这边是 JSX 语法:

<div id='one' class='two'>    <span id="spanOne">this is spanOne</span>    <span id="spanTwo">this is spanTwo</span></div>

这边是转化成的 js 语法:

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','...')


这边是 JSX 语法:

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>

也就是说:自定义的组件必须大写字母开头


二、React.createElement()
源码地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.js

作用:
创建React.Element,示例请看一、JSX语法转换到Js语法

源码:

//注意:react只写了3个参数,实际上,从第三个参数往后都是childrenexport 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内部测试的,可以不看,我简单地解释了下

② 在jQueryfn.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)

结果:

三、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=childArrayconst 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


(完)