react源碼解析-2Children
React.Children提供了對this.props.children的操作的函數。
react/src/ReactChildren文件export的函數如下
export {n forEachChildren as forEach,n mapChildren as map,n countChildren as count,n onlyChild as only,n toArray,n};n
forEachChildren
function forEachChildren(children, forEachFunc, forEachContext) {n if (children == null) {n return children;n }n var traverseContext = getPooledTraverseContext(n null,n null,n forEachFunc,n forEachContext,n );n traverseAllChildren(children, forEachSingleChild, traverseContext);n releaseTraverseContext(traverseContext);n}n
traverseContext在一個池中獲取了一個遍歷的上下文對象,這裡只是把forEachFunc和forEachContext封裝到一個對象里。
traverseAllChildren中調用了traverseAllChildrenImpl。
function traverseAllChildrenImpl(n children,n nameSoFar,n callback,n traverseContext,n) {n ...n}n
traverseAllChildrenImpl的參數children如果是數組,會遍歷children,會把每一個child遞歸調用自身。
參數children是一個單獨的react element或portal(16新增類型)時會調用callbackcallback(n traverseContext,n children,n // If its the only child, treat the name as if it was wrapped in an arrayn // so that its consistent if the number of children grows.n nameSoFar === ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,n);n
forEachChildren的回調函數是forEachSingleChild,它的內容如下,這裡重新生成的key值沒有用到
function forEachSingleChild(bookKeeping, child, name) {n var {func, context} = bookKeeping;n func.call(context, child, bookKeeping.count++);n}n
它首先在bookKeeping(遍歷上下文)里,拿到我們在forEachChildren傳入的遍歷函數和上下文,用call調用一下,我們在下面就可以拿到child和index了。
React.Children.forEach(childrenArr,(child,index)=>{n ...n});n
最後釋放上下文對象
mapChildren
function mapChildren(children, func, context) {n if (children == null) {n return children;n }n var result = [];n mapIntoWithKeyPrefixInternal(children, result, null, func, context);n return result;n}n
mapIntoWithKeyPrefixInternal的內容如下,和forEachChildren基本一致,增加了存放返回值的數組和escapedPrefix=『』。
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {n var escapedPrefix = ;n if (prefix != null) {n escapedPrefix = escapeUserProvidedKey(prefix) + /;n }n var traverseContext = getPooledTraverseContext(n array,n escapedPrefix,n func,n context,n );n traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);n releaseTraverseContext(traverseContext);n}n
不同之處在於traverseAllChildren的callback是mapSingleChildIntoContext。
function mapSingleChildIntoContext(bookKeeping, child, childKey) {n var {result, keyPrefix, func, context} = bookKeeping;nn var mappedChild = func.call(context, child, bookKeeping.count++);n if (Array.isArray(mappedChild)) {n mapIntoWithKeyPrefixInternal(n mappedChild,n result,n childKey,n emptyFunction.thatReturnsArgument,n );n } else if (mappedChild != null) {n if (isValidElement(mappedChild)) {n mappedChild = cloneAndReplaceKey(n mappedChild,n // Keep both the (mapped) and old keys if they differ, just asn // traverseAllChildren used to do for objects as childrenn keyPrefix +n (mappedChild.key && (!child || child.key !== mappedChild.key)n ? escapeUserProvidedKey(mappedChild.key) + /n : ) +n childKey,n );n }n result.push(mappedChild);n }n}n
var mappedChild = func.call(context, child, bookKeeping.count++)後如果是數組,再將結果數組通過mapIntoWithKeyPrefixInternal遞歸一遍。(之後的key值會加上.$)
得到單獨的元素時,將結果clone並且重設一遍key值,最後將結果push進result。
countChildren
function countChildren(children, context) {n return traverseAllChildren(children, emptyFunction.thatReturnsNull, null);n}n
traverseAllChildren,traverseAllChildrenImpl時有計數,將結果返回就好了。
onlyChild
function onlyChild(children) {n invariant(n isValidElement(children),n React.Children.only expected to receive a single React element child.,n );n return children;n}n
通過isValidElement判斷傳入的參數是否是「一個」react元素,否則就報錯。
export function isValidElement(object) {n return (n typeof object === object &&n object !== null &&n object.$$typeof === REACT_ELEMENT_TYPEn );n}n
toArray
function toArray(children) {n var result = [];n mapIntoWithKeyPrefixInternal(n children,n result,n null,n emptyFunction.thatReturnsArgument,n );n return result;n}n
和map類似,返回一個重設過key的元素的數組。
推薦閱讀:
※vue-router源碼分析-整體流程
※Vue.js起手式+Vue小作品實戰
※如何解讀Facebook的這款疑似Virtual DOM專利?
※遺世獨立的組件——Angular應用中的單組件構建
※Phantom.js維護者Slobodin退出,項目未來將何去何從?