其实
React.Children
这个 API 很少用,可以跳过的,但为什么还用一个章节的篇幅去诠释它呢?因为它的源码挺有意思的,对我们编程设计和编程能力有一定的帮助。
上源码
// ReactChildren.js...export { forEachChildren as forEach, mapChildren as map, countChildren as count, onlyChild as only, toArray,};复制代码
先说 mapChildren
// 上下文需求池里的获取最后一项const POOL_SIZE = 10;const traverseContextPool = [];function getPooledTraverseContext( mapResult, keyPrefix, mapFunction, mapContext,) { if (traverseContextPool.length) { const traverseContext = traverseContextPool.pop(); traverseContext.result = mapResult; traverseContext.keyPrefix = keyPrefix; traverseContext.func = mapFunction; traverseContext.context = mapContext; traverseContext.count = 0; return traverseContext; } else { return { result: mapResult, keyPrefix: keyPrefix, func: mapFunction, context: mapContext, count: 0, }; }}// 释放贯穿上下文function releaseTraverseContext(traverseContext) { traverseContext.result = null; traverseContext.keyPrefix = null; traverseContext.func = null; traverseContext.context = null; traverseContext.count = 0; if (traverseContextPool.length < POOL_SIZE) { traverseContextPool.push(traverseContext); }}function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { let escapedPrefix = ''; if (prefix != null) { escapedPrefix = escapeUserProvidedKey(prefix) + '/'; } // 贯穿上下文 const traverseContext = getPooledTraverseContext( array, escapedPrefix, func, context, ); // 贯穿整个子元素 traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); // 释放贯穿上下文 releaseTraverseContext(traverseContext);}function mapChildren(children, func, context) { if(children === null) { return children; } const result = []; mapIntoWithKeyPrefixInternal(children, result, null, func, context); return result = [];}复制代码
补个知识,
map
和forEach
最大区别在于没有return
;
先说 getPooledTraverseContext
函数吧,可以理解为上下文对象重用池,主要用于维护一个大小固定的重用池,当然了这个要配合 releaseTraverseContext
使用。
每次从这个池子去除一个对象去赋值给 traverseContext
,用完了就用 releaseTraverseContext
去释放然后再丢回池子里。这么做主要是为了提高性能,想想频繁创建和销毁一个有很多属性的对象是很消耗性能的,你说是不是?(我还能说不嘛,哈哈)