React源码解析之React.Children.map()(五)
<p> </p><p><span style="font-size: 18px"><strong>一,React.Children是什么?</strong></span></p>
<p><span style="font-size: 14px"> 是为了处理this.props.children(this.props.children表示所有组件的子节点)这个属性提供的工具,是顶层的api之一</span></p>
<p><span style="font-size: 14px"> 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> </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> 举例如下:</span></span></p>
<div class="cnblogs_Highlighter">
<div> </div>
<div> <span style="font-size: 13px; color: rgba(51, 153, 102, 1)">// this.props.children:数据类型为数组为ChildrenDemo的子节点(<span>1</span>和 <span>2</span>) React.Children.map用来遍历ChildrenDemo的子节点数目 1.<span>1</span> 2.<span>2</span><br></span></div>
<pre class="brush:javascript;gutter:true;">class ChildrenDemo extends React.Component {
render() {
return (
<ol>
{
React.Children.map(this.props.children, (child)=> {<span style="color: rgba(51, 153, 102, 1)">//callback child子节点</span>
return <li>{child}</li>
})
}
</ol>
)
}
}
class MessageList extends React.Component {
render() {
return (
<ChildrenDemo>
<span>1</span>
<span>2</span>
</ChildrenDemo>
)
}
} </pre>
</div>
<p><span style="font-size: 14px">this.props.children值有三种情况</span></p>
<p><span style="font-size: 14px">1.如果组件没有节点 值为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.如果组件有多个节点 值的数据类型为数组</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200220153106842-2076487288.png"></p>
<p>React.Children.map(this.props.children, child => ]):来遍历this.props.children的子节点 多层嵌套的 ])通过map之后平铺成一维数组</p>
<p>这里两个子节点<span>1</span>和<span>2</span>,这里每一个节点都是数组 通过React.Children.map变成一个一维数组 </p>
<p> </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">源码中: <strong>Children{</strong></span></span>map: mapChildren} //所以真正的名字是mapChildren</div>
<div>
<div><span style="font-size: 16px; color: rgba(255, 0, 0, 1)"><strong> 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=>] 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> 解析:<strong>首先定义一个result,然后执行一个函数以后,返回这个result</strong></div>
<div> </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=>]context:undefined
第二次:children:]array:[]prefix:".0"func:c=>c context:undefined
第三次:children:]array:[]prefix:".1"func:c=>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> 解析<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/'=>// aa/a//</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> ('' + text).replace(userProvidedKeyEscapeRegex, '$&/'<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<div> 解析:<strong>react对key定义的规则 如果字符串有多个/的话在字符串后面加/</strong></div>
<div> </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> 解析:创建一个对象池 复用对象 从而减少对象带来的内存暂用 和性能消耗</strong></p>
</div>
</div>
<div><strong> <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> 解析:<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 <<span style="color: rgba(0, 0, 0, 1)"> POOL_SIZE) {
traverseContextPool.push(traverseContext);
}
}</span></pre>
</div>
<p><strong> 解析:将对象属性清空并且重新放入对象池中</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时 <span>1</span>和<span>2</span>作为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)"><span>1</span> 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 < 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 && (!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)"> <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> </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"> </span></span></code></strong></pre>
<p> </p>
<p> </p>
</div><br><br>
来源:https://www.cnblogs.com/sunxiaopei/p/12335941.html
頁:
[1]