集思以广益 發表於 2020-1-17 13:58:00

React的React.createElement源码解析(一)

<h1 class="postTitle"><span style="font-size: 15px"><strong><strong>一.什么是jsx&nbsp;</strong></strong></span></h1>
<div id="cnblogs_post_body" class="blogpost-body ">
<p>jsx是语法糖&nbsp; 它是js和html的组合使用</p>
<p><span style="font-size: 14px"><strong><strong>&nbsp;为什么用jsx<strong>语法?</strong></strong></strong></span></p>
<p>&nbsp;高效定义模版,通过babel编译后使用 不会带来性能问题</p>
<p><span style="font-size: 15px"><strong>二.jsx语法转化为js语法&nbsp;</strong></span></p>
<p>&nbsp;&nbsp;jsx语法通过babel转化为js语法 内部调用了React.<span style="color: rgba(0, 128, 0, 1)">createElement()</span>方法&nbsp;</p>
<p>&nbsp; html标签</p>
<p>&nbsp; &nbsp;<img src="https://img2018.cnblogs.com/common/1853661/202001/1853661-20200110202737069-1831215256.png"></p>
<p>自定义组件</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202001/1853661-20200113224114923-2028308476.png"></p>
<p>&nbsp;React.Fragment组件</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202001/1853661-20200117102837870-1519722483.png"></p>
<p>&nbsp;</p>
<p><span style="color: rgba(255, 0, 0, 1)">React.createElement(标签,属性props对象,子节点1,子节点2.....)&nbsp;</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">1.参数:标签名,属性对象,子节点&nbsp; &nbsp;返回值:虚拟dom对象</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">2.标签:1.字符串 2.组件(自定义组件:函数组件/class组件,react原生组件:React.Fragment等)&nbsp;</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">    一般组件首字母大写 如果bable转化时 发现当前标签的首字母为大写 则表示当前的标签是一个函数名称 否则当前标签为一个字符串</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">3.属性props对象:写在标签上的属性集合 一般为对象</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">4.子节点:表示子节点的集合 一般从React.createElement的第三个参数开始,如果对于当前标签的子节点为字符串则参数值直接为字符串 </span></p>
<p><span style="color: rgba(0, 0, 0, 1)">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 如果当前</span>节点的子节点不是字符串 则会生成新的React.createElement</p>
<p><span style="color: rgba(0, 0, 0, 1)">如:&lt;ul&gt;&lt;li&gt;我是li的子节点&lt;/li&gt;&lt;/ul&gt;&nbsp; 1.ul而言 子节点:li&nbsp; 2.li而言 子节点我是li的子节点</span></p>
<p>虚拟dom:</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202001/1853661-20200110203642020-159494364.png"></p>
<p>&nbsp;调用createElement函数 返回ReactElement:该标签名放到type上 该标签的属性和该标签的子节点放到与该标签同级的props上</p>
<p>&nbsp;</p>
<p><span style="font-size: 15px"><strong>三.React.createElement(type,config[...children])源码分析</strong></span></p>
<p>作用:根据指定的第一个参数 创建一个react元素</p>
<p>源码解析:<span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(136, 136, 136, 1); color: rgba(0, 128, 0, 1)"><span style="background-color: rgba(255, 255, 255, 1)">//type:节点名称(函数/字符串) config:节点名称属性</span><span style="background-color: rgba(255, 255, 255, 1)">(</span></span></span><span style="color: rgba(0, 128, 0, 1)">对象) children:节点名称的子节点(字符串/新的React.createElement</span></p>
<div class="cnblogs_code">
<pre></pre>
<pre></pre>
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> createElement(type, config, children) { <span style="background-color: rgba(136, 136, 136, 1); color: rgba(0, 128, 0, 1)"><span style="background-color: rgba(255, 255, 255, 1)"><br></span></span></span><span style="color: rgba(0, 0, 255, 1)">      var</span><span style="color: rgba(0, 0, 0, 1)"> propName;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">提取保留名称</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> props =<span style="color: rgba(0, 0, 0, 1)"> {};

      </span><span style="color: rgba(0, 0, 255, 1)">var</span> key = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> ref = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> self = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> source = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">标签的属性不为空时 说明标签有属性值 特殊处理:把key和ref赋值给单独的变量</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (config != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">有合理的ref</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hasValidRef(config)) {
          ref </span>=<span style="color: rgba(0, 0, 0, 1)"> config.ref;
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">有合理的key </span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hasValidKey(config)) {
          key </span>= '' +<span style="color: rgba(0, 0, 0, 1)"> config.key;
      }

      self </span>= config.__self === undefined ? <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> : config.__self;
      source </span>= config.__source === undefined ? <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> : config.__source;

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">config中剩余属性,且不是原生属性(RESERVED_PROPS对象的属性),则添加到新props对象中</span>
      <span style="color: rgba(0, 0, 255, 1)">for</span> (propName <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> config) {
          </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hasOwnProperty.call(config, propName) &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">RESERVED_PROPS.hasOwnProperty(propName)) {
            props </span>=<span style="color: rgba(0, 0, 0, 1)"> config; <span style="color: rgba(0, 128, 0, 1)">//config去除key/ref 其他属性的放到props对象中</span>
          }
      }
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Children can be more than one argument, and those are transferred onto</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the newly allocated props object.</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子元素数量(第三个参数以及之后参数都是子元素 兄弟节点)</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> childrenLength = arguments.length - 2<span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (childrenLength === 1<span style="color: rgba(0, 0, 0, 1)">) {
      props.children </span>=<span style="color: rgba(0, 0, 0, 1)"> children;
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (childrenLength &gt; 1<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> childArray =<span style="color: rgba(0, 0, 0, 1)"> Array(childrenLength); <span style="color: rgba(0, 128, 0, 1)">//声明一个数组
      </span></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">依次将children push到数组中</span>
      <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> i = 0; i &lt; childrenLength; i++<span style="color: rgba(0, 0, 0, 1)">) {
          childArray </span>= arguments;
      }

      {
          </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">冻结array 返回原来的childArray且不能被修改 防止有人修改库的核心对象 冻结对象大大提高性能</span>
          <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (Object.freeze) {
            Object.freeze(childArray);
          }
      }
      props.children </span>=<span style="color: rgba(0, 0, 0, 1)"> childArray; <span style="color: rgba(0, 128, 0, 1)">//父组件内部通过this.props.children获取子组件的值</span>
      }

      </span><span style="color: rgba(0, 128, 0, 1)">//为</span><span style="color: rgba(0, 128, 0, 1)">子组件设置默认值 一般针对的是组件<br></span>      //<span style="color: rgba(0, 128, 0, 1)">class com extends React.component 则com.defaultProps获取当前组件自己的静态方法 </span><span style="color: rgba(0, 128, 0, 1)"><br></span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (type &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> type.defaultProps) { <span style="color: rgba(0, 128, 0, 1)">//如果当前组件中有默认的defaultProps则把当前组件的默认内容 定义到defaultProps中
      </span></span><span style="color: rgba(0, 0, 255, 1)">var</span> defaultProps =<span style="color: rgba(0, 0, 0, 1)"> type.defaultProps;

      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (propName <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> defaultProps) {
          </span><span style="color: rgba(0, 0, 255, 1)">if</span> (props ===<span style="color: rgba(0, 0, 0, 1)"> undefined) { <span style="color: rgba(0, 128, 0, 1)">//如果父组件中对应的值为undefinde 则把默认值赋值赋值给props当作props的属性</span>
            props </span>=<span style="color: rgba(0, 0, 0, 1)"> defaultProps;
          }
      }
      }

      {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">一旦ref或者key存在</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (key ||<span style="color: rgba(0, 0, 0, 1)"> ref) {
          </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果type是组件的话</span>
          <span style="color: rgba(0, 0, 255, 1)">var</span> displayName = <span style="color: rgba(0, 0, 255, 1)">typeof</span> type === 'function' ? type.displayName || type.name || 'Unknown'<span style="color: rgba(0, 0, 0, 1)"> : type;
          </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (key) {
            defineKeyPropWarningGetter(props, displayName);
          }

          </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (ref) {
            defineRefPropWarningGetter(props, displayName);
          }
      }
      }

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">props:1.config的属性值 2.children的属性(字符串/数组)3.default的属性值</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
    }</span></pre>
</div>
<p>&nbsp; 部分代码解析:</p>
<p>&nbsp;&nbsp;(1) Object.prototype.hasOwnProperty()&nbsp; &nbsp;</p>
<div class="cnblogs_code">
<pre>/<span style="color: rgba(0, 128, 0, 1)">/判断某一个属性是否在实例对象本身上,而不是在原型上 返回值:布尔值</span>
let obj = { a: 1 };
obj.hasOwnProperty("a") //true
obj.hasOwnProperty("toString")//false</pre>
</div>
<div>
<div>&nbsp;(2) Object.getOwnPropertyDescriptor(obj, props)</div>
<div>&nbsp; &nbsp; &nbsp; Object.getOwnPropertyDescriptors(obj)</div>
<div>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
<pre>let obj = {
foo: 123,
get bar() { return '123' }
};
<span style="color: rgba(0, 128, 0, 1)">//返回对象指定某一个属性的描述对象</span>
Object.getOwnPropertyDescriptor(obj, 'bar')
<span style="color: rgba(0, 128, 0, 1)">//返回对象所有属性的描述对象</span>
Object.getOwnPropertyDescriptors(obj)</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
</div>
<p>返回值</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202001/1853661-20200115170452712-1076452457.png"></p>
</div>
</div>
<div>
<div>2.hasValidKey</div>
<div>作用:是否设置了key属性</div>
<div>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
<pre> function hasValidKey(config) {
      {
      if (hasOwnProperty.call(config, 'key')) {
          var getter = Object.getOwnPropertyDescriptor(config, 'key').get;

          if (getter &amp;&amp; getter.isReactWarning) {
            return false;
          }
      }
      }

      return config.key !== undefined;
    }</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
</div>
<p><span style="font-size: 15px"><strong>四,ReactElement<strong>源码分析</strong></strong></span></p>
<p>作用:返回一个虚拟dom对象</p>
<p>&nbsp;源码:</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
<pre>   var ReactElement = function (type, key, ref, self, source, owner, props) {
      var element = {
      <span style="color: rgba(0, 128, 0, 1)">//因为react最终渲染dom时候,确保是react.createElement类型 需要判断$$typeof===REACT_ELEMENT_TYPE</span>
      $$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
      };

      {
      // 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.
      //WeakMap
      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) {
         <span style="color: rgba(0, 128, 0, 1)"> //代码性能优化:将element的一些属性配置为不可配置 提高性能</span>
          Object.freeze(element.props);
          Object.freeze(element);
      }
      }

      return element;<span style="color: rgba(0, 128, 0, 1)">//返回虚拟dom对象</span>
    };</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="https://common.cnblogs.com/images/copycode.gif"></span></div>
</div>
<pre>&nbsp;</pre>
</div>
</div>
</div>
<pre><span><span style="background-color: rgba(136, 136, 136, 1); color: rgba(0, 128, 0, 1)"><span style="background-color: rgba(255, 255, 255, 1)">节点名称</span></span></span></pre><br><br>
来源:https://www.cnblogs.com/sunxiaopei/p/12205447.html
頁: [1]
查看完整版本: React的React.createElement源码解析(一)