function ChildrenDemo(props) {
console.log(props.children, 'children30');
console.log(React.Children.map(props.children, item => [item, [item, [item]]]), 'children31');
// console.log(React.Children.map(props.children,item=>item),'children31')
return props.children;
}
export default ()=>(
<ChildrenDemo>
<span key={'.0/'}>1</span>
<span>2</span>
</ChildrenDemo>
)
它的输出是:
[object Array]: [Object, Object, Object, Object, Object, Object]
0: Object
: Object
$$typeof: <No implicit conversion of Symbol to String>
_owner: Object
_self: Object
_source: Object
_store: Object
key: ".$.0///.$.0/"
props: Object
ref: null
type: "span"
__proto__: Object
1: Object
2: Object
3: Object
4: Object
5: Object
length: 6
由于 csdn图片上传有bug。。我就这么大概写一写。
map的功能呢,是将数组一层一层展开,并赋予不同的key值;
二、React.Children.map()
源码:
// React.Children.map(props.children,item=>[item,[item,] ])
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
const result = [];
//进行基本的判断和初始化后,调用该方法
//props.children,[],null,(item)=>{return [item,[item,] ]},undefined
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
export {
//as就是重命名了,map即mapChildren
forEachChildren as forEach,
mapChildren as map,
countChildren as count,
onlyChild as only,
toArray,
};
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
children在mapIntoWithKeyPrefixInternal(children, result, null, func, context); 运行一圈以后,就返回result
三、mapIntoWithKeyPrefixInternal()
//第一次:props.children , [] , null , (item)=>{return [item,[item,] ]} , undefined
//第二次:[item,[item,] ] , [] , .0 , c => c , undefined
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
let escapedPrefix = '';
//如果字符串中有连续多个 / 的话,在匹配的字串后再加 /
if (prefix != null) {
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
}
//从pool中找一个对象
//[],'',(item)=>{return [item,[item,] ]},undefined
//traverseContext=
// {
// result:[],
// keyPrefix:'',
// func:(item)=>{return [item,[item,] ]},
// context:undefined,
// count:0,
// }
const traverseContext = getPooledTraverseContext(
array,
escapedPrefix,
func,
context,
);
//将嵌套的数组展平
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
releaseTraverseContext(traverseContext);//traverseContext对象里面的属性值设置为null
}
四、traverseAllChildren
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
function traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
const type = typeof children;
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}
let invokeCallback = false;
if (children === null) {
invokeCallback = true;
} else {
switch (type) {
case 'string'://有可能是VText;
case 'number':
invokeCallback = true;
break;
case 'object'://如果是对象
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE://对象是普通组件
case REACT_PORTAL_TYPE://对象是Protals组件;
invokeCallback = true;
}
}
}
if (invokeCallback) {
callback(
traverseContext,
children,
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}
let child;
let nextName;
let subtreeCount = 0; // Count of children found in the current subtree.
const nextNamePrefix =
nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
if (Array.isArray(children)) {//如果是数组。。。
for (let i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseAllChildrenImpl(//再调用一次该函数;subtreeCount不重要;
child,
nextName,
callback,
traverseContext,
);
}
} else {
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
warning(
didWarnAboutMaps,
'Using Maps as children is unsupported and will likely yield ' +
'unexpected results. Convert it to a sequence/iterable of keyed ' +
'ReactElements instead.',
);
didWarnAboutMaps = true;
}
}
const iterator = iteratorFn.call(children);
let step;
let ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else if (type === 'object') {
let addendum = '';
if (__DEV__) {
addendum =
' If you meant to render a collection of children, use an array ' +
'instead.' +
ReactDebugCurrentFrame.getStackAddendum();
}
const childrenString = '' + children;
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
: childrenString,
addendum,
);
}
}
return subtreeCount;
}
解析
上面的函数是将组件元素或者Vtext元素放入回调函数中滚一圈。如果是数组,那么递归它的子元素,直到它的子元素是组件元素或者Vtext元素为止;
五、mapSingleChildIntoContext
上面的callback就是mapSingleChildIntoContext
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const {result, keyPrefix, func, context} = bookKeeping;
let mappedChild = func.call(context, child, bookKeeping.count++);
if (Array.isArray(mappedChild)) {
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
} else if (mappedChild != null) {
if (isValidElement(mappedChild)) {
mappedChild = cloneAndReplaceKey(
mappedChild,
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);//放入result数组;
}
}
以上就是核心的思路。
零星的方法:
六、getPooledTraverseContext
/对象池的最大容量为10
const POOL_SIZE = 10;
//对象池
const traverseContextPool = [];
//[],'',(item)=>{return [item,[item,] ]},undefined
function getPooledTraverseContext(
mapResult,
keyPrefix,
mapFunction,
mapContext,
) {
//如果对象池内存在对象,则出队一个对象,
//并将arguments的值赋给对象属性
//最后返回该对象
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 {
//{
// result:[],
// keyPrefix:'',
// func:(item)=>{return [item,[item,] ]},
// context:undefined,
// count:0,
// }
return {
result: mapResult,
keyPrefix: keyPrefix,
func: mapFunction,
context: mapContext,
count: 0,
};
}
}
将 mapResult,keyPrefix,mapFunction,mapContext,放进同一个对象;
在每次map()
的过程中,每次递归都会用到traverseContext
对象,
创建traverseContextPool
对象池的目的,就是复用里面的对象,
以减少内存消耗,并且在map()
结束时,
将复用的对象初始化,并push
进对象池中(releaseTraverseContext
),以供下次map()
时使用