一人难称百人心 發表於 2020-2-22 16:19:00

React源码解析之React.Children.map()(五)

<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>一,React.Children是什么?</strong></span></p>
<p><span style="font-size: 14px">&nbsp; &nbsp; &nbsp;是为了处理this.props.children(this.props.children表示所有组件的子节点)这个属性提供的工具,是顶层的api之一</span></p>
<p><span style="font-size: 14px">&nbsp; &nbsp;React.children的用处:<span style="text-decoration: underline"><span style="color: rgba(255, 0, 0, 1); text-decoration: underline">https://www.cnblogs.com/sunxiaopei/p/13805645.html</span></span></span></p>
<p><span style="font-size: 18px"><strong>二,<strong>React.Children.map的结构及举例</strong></strong></span></p>
<p><span style="font-size: 18px"><strong><strong>&nbsp;</strong></strong><span style="font-size: 14px">结构:<span style="color: rgba(255, 0, 0, 1)">React.Children.map(object children,function fn [, object context])</span></span></span></p>
<p><span style="font-size: 18px"><span style="font-size: 14px"><br>&nbsp;举例如下:</span></span></p>
<div class="cnblogs_Highlighter">
<div>&nbsp;</div>
<div>&nbsp; &nbsp;<span style="font-size: 13px; color: rgba(51, 153, 102, 1)">// this.props.children:数据类型为数组为ChildrenDemo的子节点(&lt;span&gt;1&lt;/span&gt;和 &lt;span&gt;2&lt;/span&gt;)&nbsp; &nbsp;React.Children.map用来遍历ChildrenDemo的子节点数目 1.&lt;span&gt;1&lt;/span&gt; 2.&lt;span&gt;2&lt;/span&gt;<br></span></div>
<pre class="brush:javascript;gutter:true;">class ChildrenDemo extends React.Component {
render() {
    return (
      &lt;ol&gt;
      {
          React.Children.map(this.props.children, (child)=&gt; {<span style="color: rgba(51, 153, 102, 1)">//callback child子节点</span>
            return &lt;li&gt;{child}&lt;/li&gt;
          })
      }
      &lt;/ol&gt;
    )
}
}
class MessageList extends React.Component {
render() {
    return (
      &lt;ChildrenDemo&gt;
      &lt;span&gt;1&lt;/span&gt;
      &lt;span&gt;2&lt;/span&gt;
      &lt;/ChildrenDemo&gt;
    )
}
}  </pre>
</div>
<p><span style="font-size: 14px">this.props.children值有三种情况</span></p>
<p><span style="font-size: 14px">1.如果组件没有节点&nbsp; &nbsp;值为undefined</span></p>
<p><span style="font-size: 14px"><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220152847745-246756745.png"></span></p>
<p>2.如果组件有一个节点 值的数据类型为对象</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220153015616-2093281390.png"></p>
<p>3.如果组件有多个节点&nbsp; 值的数据类型为数组</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220153106842-2076487288.png"></p>
<p>React.Children.map(this.props.children, child =&gt; ]):来遍历this.props.children的子节点 多层嵌套的&nbsp;])通过map之后平铺成一维数组</p>
<p>这里两个子节点&lt;span&gt;1&lt;/span&gt;和&lt;span&gt;2&lt;/span&gt;,这里每一个节点都是数组 通过React.Children.map变成一个一维数组&nbsp;</p>
<p>&nbsp;</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220161014621-1479655569.png"></p>
<div><strong><span style="font-size: 18px">三、React.Children.map()中map方法的源码解析</span></strong></div>
<div><span style="font-size: 14px"><span style="font-size: 14px">源码中:&nbsp;<strong>Children{</strong></span></span>map: mapChildren}&nbsp; //所以真正的名字是mapChildren</div>
<div>
<div><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>&nbsp;mapChildren()</strong></span></div>
<div>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">children:被遍历的子组件</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">func:单个子组件需要执行的函数 </span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">context:func执行时候,this指针指的对象</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> mapChildren(children, func, context) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (children == <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)">return</span><span style="color: rgba(0, 0, 0, 1)"> children;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> result =<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)">this.props.children   [] null child=&gt;] undefined</span>
      <span style="color: rgba(255, 0, 0, 1)"><strong>mapIntoWithKeyPrefixInternal</strong></span>(children, result, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">, func, context);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">执行完mapIntoWithKeyPrefixInternal方法后 返回result</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
    }</span></pre>
</div>
<div>&nbsp;解析:<strong>首先定义一个result,然后执行一个函数以后,返回这个result</strong></div>
<div>&nbsp;</div>
<div><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>mapIntoWithKeyPrefixInternal()</strong></span></div>
<div>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
   第一次:children:    array:[]prefix:nullfunc:child=&gt;]context:undefined
   第二次:children:]array:[]prefix:".0"func:c=&gt;c                  context:undefined
   第三次:children:]array:[]prefix:".1"func:c=&gt;c                  context:undefined
    </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> escapedPrefix = ''<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 如果字符串有多个/ 在匹配的字符串后面加一个/ 一般第二层递归用到 第一层prefix为null</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (prefix != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
      escapedPrefix </span>= <span style="color: rgba(255, 0, 0, 1)"><strong>escapeUserProvidedKey</strong></span>(prefix) + '/'<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)">从traverseContextPool里面里面获取一个context</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> traverseContext =<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"><strong> getPooledTraverseContext</strong></span>(array, escapedPrefix, func, context);
      </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, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"><strong>      traverseAllChildren</strong></span>(children, mapSingleChildIntoContext, traverseContext);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">context对象清空然后放回到traverseContextPool里面</span>
<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"><strong>      releaseTraverseContext</strong></span>(traverseContext);
    }</span></pre>
</div>
<p><strong>&nbsp;解析<span style="color: rgba(0, 0, 0, 1)">:escapeUserProvidedKey()/getPooledTraverseContext()/traverseAllChildren()/releaseTraverseContext()</span>函数的包裹器</strong></p>
<p><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>escapeUserProvidedKey()</strong></span></p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> escapeUserProvidedKey(text) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果字符串中有多个/的话 在匹配的字符后加/</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">let a='aa/a/'=&gt;// aa/a//</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> ('' + text).replace(userProvidedKeyEscapeRegex, '$&amp;/'<span style="color: rgba(0, 0, 0, 1)">);
    }</span></pre>
</div>
<div>&nbsp;解析:<strong>react对key定义的规则 如果字符串有多个/的话在字符串后面加/</strong></div>
<div>&nbsp;</div>
<div>
<div><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>getPooledTraverseContext()</strong></span></div>
<div>
<div class="cnblogs_code">
<pre> <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> POOL_SIZE = 10;    <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> traverseContextPool = [];<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">全局变量 对象池 有多少个数组,就有多少个对象,这就是traverseContextPool设置在这里的含义</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) {
      </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)">if</span> (traverseContextPool.length) {<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> traverseContext = traverseContextPool.pop();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">pop()方法用于删除并返回数组的最后一个元素。</span>
      traverseContext.result =<span style="color: rgba(0, 0, 0, 1)"> mapResult;
      traverseContext.keyPrefix </span>=<span style="color: rgba(0, 0, 0, 1)"> keyPrefix;
      traverseContext.func </span>=<span style="color: rgba(0, 0, 0, 1)"> mapFunction;
      traverseContext.context </span>=<span style="color: rgba(0, 0, 0, 1)"> mapContext;
      traverseContext.count </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> traverseContext;<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)">else</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)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
          result: mapResult,
          keyPrefix: keyPrefix,
          func: mapFunction,
          context: mapContext,
          count: </span>0<span style="color: rgba(0, 0, 0, 1)">
      };
      }
    }</span></pre>
</div>
<p><strong>&nbsp;解析:创建一个对象池 复用对象 从而减少对象带来的内存暂用 和性能消耗</strong></p>
</div>
</div>
<div><strong>&nbsp;<span style="font-size: 16px; color: rgba(255, 0, 0, 1)"> traverseAllChildren()</span></strong></div>
<div>
<div class="cnblogs_code">
<pre>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">traverseAllChildrenImpl的触发器</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> traverseAllChildren(children, callback, traverseContext) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (children == <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)">return</span> 0<span style="color: rgba(0, 0, 0, 1)">;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(255, 0, 0, 1)"><strong>traverseAllChildrenImpl</strong></span>(children, ''<span style="color: rgba(0, 0, 0, 1)">, callback, traverseContext);
    }</span></pre>
</div>
<p><strong>&nbsp;&nbsp;解析:<span style="color: rgba(0, 0, 0, 1)">traverseAllChildrenImpl的触发器</span></strong></p>
<p><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>releaseTraverseContext()</strong></span></p>
</div>
<div>
<div class="cnblogs_code">
<pre> <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)">function</span><span style="color: rgba(0, 0, 0, 1)"> releaseTraverseContext(traverseContext) {
      traverseContext.result </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      traverseContext.keyPrefix </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      traverseContext.func </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      traverseContext.context </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      traverseContext.count </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (traverseContextPool.length &lt;<span style="color: rgba(0, 0, 0, 1)"> POOL_SIZE) {
      traverseContextPool.push(traverseContext);
      }
    }</span></pre>
</div>
<p><strong>&nbsp;解析:将对象属性清空并且重新放入对象池中</strong></p>
<pre><strong><span style="font-size: 16px; color: rgba(255, 0, 0, 1)">traverseAllChildrenImpl()</span><br></strong></pre>
</div>
</div>
</div>
</div>
<div>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">核心递归函数 目的为展平数组</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">对于可以循环的children 都会重复调用traverseAllChildrenImpl 直到一个节点 然后调用callback</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> type = <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> children;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (type === 'undefined' || type === 'boolean'<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)">undefined null都被认为为null</span>
      children = <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)">调用fun的flag</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> invokeCallback = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (children === <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
      invokeCallback </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (type) {
          </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'string'<span style="color: rgba(0, 0, 0, 1)">:
          </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'number'<span style="color: rgba(0, 0, 0, 1)">:
            invokeCallback </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</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)">如果props.children单个节点</span>
          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">递归traverAllChildenTml时 &lt;span&gt;1&lt;/span&gt;和&lt;span&gt;2&lt;/span&gt;作为child</span>
          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">必会invokeCallback=true</span>
          <span style="color: rgba(0, 0, 255, 1)">case</span> 'object'<span style="color: rgba(0, 0, 0, 1)">:
            </span><span style="color: rgba(0, 0, 255, 1)">switch</span> (children.$$<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> REACT_ELEMENT_TYPE:
            </span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> REACT_PORTAL_TYPE:
                invokeCallback </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            }

      }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (invokeCallback) {
      <strong><span style="color: rgba(255, 0, 0, 1)">callback</span></strong>(traverseContext, children, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> If it's the only child, treat the name as if it was wrapped in an array</span>
          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> so that it's consistent if the number of children grows.</span>
          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果只有一个节点 直接调用callback</span>
          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">&lt;span&gt;1&lt;/span&gt; key=".0"</span>
          nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0<span style="color: rgba(0, 0, 0, 1)">) : nameSoFar);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> 1<span style="color: rgba(0, 0, 0, 1)">;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> child;
      </span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> nextName;
      </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> subtreeCount = 0; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Count of children found in the current subtree.</span>

      <span style="color: rgba(0, 0, 255, 1)">var</span> nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar +<span style="color: rgba(0, 0, 0, 1)"> SUBSEPARATOR;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Array.isArray(children)) {<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)">for</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> i = 0; i &lt; children.length; i++) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">循环遍历多个节点</span>
          child =<span style="color: rgba(0, 0, 0, 1)"> children;
          </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不手动设置key的话 第一层第一个时。0 第二个。1</span>
          nextName = nextNamePrefix +<span style="color: rgba(0, 0, 0, 1)"> getComponentKey(child, i);
          </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">把child作为参数 进行递归 直到为单个节点的时候 去调用callback</span>
          subtreeCount +=<span style="color: rgba(0, 0, 0, 1)"> traverseAllChildrenImpl(child, nextName, callback, traverseContext);
      }
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> iteratorFn =<span style="color: rgba(0, 0, 0, 1)"> getIteratorFn(children);

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> iteratorFn === 'function'<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)"> Warn about using Maps as children</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (iteratorFn ===<span style="color: rgba(0, 0, 0, 1)"> children.entries) {
            </span>!didWarnAboutMaps ? warning$1(<span style="color: rgba(0, 0, 255, 1)">false</span>, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.') : <span style="color: rgba(0, 0, 255, 1)">void</span> 0<span style="color: rgba(0, 0, 0, 1)">;
            didWarnAboutMaps </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            }
          }

          </span><span style="color: rgba(0, 0, 255, 1)">var</span> iterator =<span style="color: rgba(0, 0, 0, 1)"> iteratorFn.call(children);
          </span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> step;
          </span><span style="color: rgba(0, 0, 255, 1)">var</span> ii = 0<span style="color: rgba(0, 0, 0, 1)">;

          </span><span style="color: rgba(0, 0, 255, 1)">while</span> (!(step =<span style="color: rgba(0, 0, 0, 1)"> iterator.next()).done) {
            child </span>=<span style="color: rgba(0, 0, 0, 1)"> step.value;
            nextName </span>= nextNamePrefix + getComponentKey(child, ii++<span style="color: rgba(0, 0, 0, 1)">);
            subtreeCount </span>+=<span style="color: rgba(0, 0, 0, 1)"> traverseAllChildrenImpl(child, nextName, callback, traverseContext);
          }
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (type === 'object'<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)">如果是纯对象</span>
          <span style="color: rgba(0, 0, 255, 1)">var</span> addendum = ''<span style="color: rgba(0, 0, 0, 1)">;

          {
            addendum </span>= ' If you meant to render a collection of children, use an array ' + 'instead.' +<span style="color: rgba(0, 0, 0, 1)"> ReactDebugCurrentFrame.getStackAddendum();
          }

          </span><span style="color: rgba(0, 0, 255, 1)">var</span> childrenString = '' +<span style="color: rgba(0, 0, 0, 1)"> children;

          {
            {
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span> Error("Objects are not valid as a React child (found: " + (childrenString === '' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString) + ")." +<span style="color: rgba(0, 0, 0, 1)"> addendum);
            }
          }
      }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> subtreeCount;
    }</span></pre>
</div>
<p><strong>解析:当type的值为不可遍历的undefined/boolean/string/number/object时候 则直接调用callback 回调函数</strong></p>
<p><strong>核心递归函数 目的为展平数组,对于可以循环的children 都会重复调用<span style="color: rgba(0, 0, 0, 1)">0 直到一个节点 然后调用callback也就是mapSingleChildIntoContext</span></strong></p>
<pre><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>mapSingleChildIntoContext()</strong></span></pre>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">复制除了key以外的属性 替换key属性 将其放到result中 bookKeeping:context对象</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> mapSingleChildIntoContext(bookKeeping, child, childKey) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> result =<span style="color: rgba(0, 0, 0, 1)"> bookKeeping.result,
      keyPrefix </span>=<span style="color: rgba(0, 0, 0, 1)"> bookKeeping.keyPrefix,
      func </span>=<span style="color: rgba(0, 0, 0, 1)"> bookKeeping.func,
      context </span>=<span style="color: rgba(0, 0, 0, 1)"> bookKeeping.context;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用fun回调函数</span>
      <span style="color: rgba(0, 0, 255, 1)">var</span> mappedChild = func.call(context, child, bookKeeping.count++<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)">对每一个节点 如果是数组 进行递归</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (Array.isArray(mappedChild)) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果返回一个数组 这里返回的是]是一维数组</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">大递归 这次 不再调用fun 如果调用fun则无限循环 所以直接返回c</span>
      <span style="color: rgba(255, 0, 0, 1)"><strong>mapIntoWithKeyPrefixInternal</strong></span>(mappedChild, result, childKey, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (c) {
          </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> c;
      });
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (mappedChild != <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)">if</span> (isValidElement(mappedChild)) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">isValidElement 判断是否是合理的reactElement元素</span>
          mappedChild = <span style="color: rgba(255, 0, 0, 1)"><strong>cloneAndReplaceKey</strong></span>(mappedChild, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Keep both the (mapped) and old keys if they differ, just as</span>
            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> traverseAllChildren used to do for objects as children</span>
            keyPrefix + (mappedChild.key &amp;&amp; (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') +<span style="color: rgba(0, 0, 0, 1)"> childKey);
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">替换一下key推入到result中</span>
<span style="color: rgba(0, 0, 0, 1)">      result.push(mappedChild);
      }
    }</span></pre>
</div>
<p><strong>解析:<span style="color: rgba(0, 0, 0, 1)">&nbsp;<code>mapSingleChildIntoContext</code>这个方法其实就是调用<code>React.Children.map(children, callback)</code>这里的<code>callback</code>,就是我们传入的第二个参数,并得到<code>map</code>之后的结果。注意重点来了,如果<code>map</code>之后的节点还是一个数组,那么再次进入<code>mapIntoWithKeyPrefixInternal</code>,那么这个时候我们就会再次从<code>pool</code>里面去<code>context</code>了,而<code>pool</code>的意义大概也就是在这里了,如果循环嵌套多了,可以减少很多对象创建和<code>gc</code>的损耗,而如果不是数组并且是一个合规的<code>ReactElement</code>,就触达顶点了,替换一下<code>key</code>就推入<code>result</code>了</span></strong></p>
<pre><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong>cloneAndReplaceKey()</strong></span></pre>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回一个新的reactElement 替换了newKey 其他的都是老得reactElement</span>
    <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> cloneAndReplaceKey(oldElement, newKey) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> newElement =<span style="color: rgba(0, 0, 0, 1)"> ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> newElement;
    }</span></pre>
</div>
<p><strong>解析:返回一个新的reactElement 替换了newKey 其他的都是老得reactElement</strong></p>
<p>&nbsp;</p>
<p>流程图如下</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220232755811-194777644.jpg"></p>
<pre><strong><code class="lang-js"><span class="hljs-function"><span class="hljs-title">&nbsp;</span></span></code></strong></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><br><br>
来源:https://www.cnblogs.com/sunxiaopei/p/12335941.html
頁: [1]
查看完整版本: React源码解析之React.Children.map()(五)