From d52b7aa5aaabf2c42a25fcc9819614f2f76e8476 Mon Sep 17 00:00:00 2001
From: ailin <5110360+fenglinlin@user.noreply.gitee.com>
Date: Tue, 7 Apr 2020 18:05:46 +0800
Subject: [PATCH 1/2] =?UTF-8?q?update=20src/react/ReactChildren.js.=20?=
=?UTF-8?q?=E5=A2=9E=E5=8A=A0mapchildren=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/react/ReactChildren.js | 203 ++++++++++++++++++++++++++++++++++++-
1 file changed, 202 insertions(+), 1 deletion(-)
diff --git a/src/react/ReactChildren.js b/src/react/ReactChildren.js
index bbc07d8..5de2f60 100644
--- a/src/react/ReactChildren.js
+++ b/src/react/ReactChildren.js
@@ -1,8 +1,209 @@
+//这个方法接受3个参数children、func和context。
+//children就是将要被遍历的子组件数组,func是对单个子组件需要执行的函数,context则是func执行时this指针所指向的对象。
function mapChildren(children, func, context) {
//TODO实现此mapChildren方法
- return children;
+ if (children == null) {
+ return children;
+ }
+ const result = [];
+ mapIntoWithKeyPrefixInternal(children,result,null,func,context);
+ return result
}
+/**
+ * @param {*} props.children
+ * @param {Array} array 结果数组
+ * @param {*} prefix null
+ * @param {function} func 每个孩子调用的 callback
+ * @param {obj} context func调用的上下文
+ */
+function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
+ // 和 prefix 前缀相关的可以暂时不用管
+ /*let escapedPrefix = '';
+ if (prefix != null) {
+ escapedPrefix = escapeUserProvidedKey(prefix) + '/';
+ }*/
+ // 从 存储 context 池子中拿取一个空对象来用
+ const traverseContext = getPooledTraverseContext(
+ array,
+ escapedPrefix,
+ func,
+ context,
+ );
+ traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
+ releaseTraverseContext(traverseContext);
+ }
+ // context 池子的大小
+const POOL_SIZE = 10;
+// context 池
+const traverseContextPool = [];
+/**
+ * 从 context 池子中拿到一个空的 context 对象来用,然后将传进去的参数添加到 context 中,
+ * 并添加 count,初始值为 0,用来记录这个遍历过程中每一个被遍历到的 child 的顺序, 并被当做 mapfunction 的第三个 index 参数。然后返回 context
+ * @param {array} mapResult 结果数组
+ * @param {string} keyPrefix 前缀
+ * @param {funcgtion} mapFunction 每一个孩子调用的 callback
+ * @param {obj} mapContext mapFunction 调用的时候的上下文
+ */
+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,
+ };
+ }
+}
+
+// 释放使用过的 context,将参数置为初始值,如果线程池没有满,那么就讲这个
+// 使用过的 context 添加进去。这样做的目的是为了防止频繁的分配内存,影响性能。
+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);
+ }
+}
+/**
+ * 遍历 children 实现
+ * @param {?*} props.children
+ * @param {!string} nameSoFar Name of the key path so far.
+ * @param {!function} callback 对每个找到的 children 调用的方法,在它的内部会调用我们使用的时候传入的那个 mapFunction,然后把结果 push 到 result 数组中。
+ * @param {?*} traverseContext 用于在遍历过程中传递信息。
+ * @return {!number} 返回当前参数 children 下有多少个孩子
+ */
+function traverseAllChildrenImpl(
+ children,
+ nameSoFar,
+ callback,
+ traverseContext,
+ ) {
+ // -------------------------- 首先处理 单个 children 的情况
+ const type = typeof children;
+
+ if (type === 'undefined' || type === 'boolean') {
+ children = null;
+ }
+
+ let invokeCallback = false;
+ // 为 null 也会调用
+ if (children === null) {
+ invokeCallback = true;
+ } else {
+ // 单个节点可能存在下面几种情况
+ switch (type) {
+ case 'string':
+ case 'number':
+ invokeCallback = true;
+ break;
+ case 'object':
+ switch (children.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ invokeCallback = true;
+ }
+ }
+ }
+ // 如果 children 是单个节点
+ if (invokeCallback) {
+ callback(
+ traverseContext,
+ children,
+ nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
+ );
+ // 只有一个节点 那么 children 数量就是 1, 该函数返回 children 的数量,所以这里直接返回 1
+ return 1;
+ }
+ // -------------------------- 处理children 是 Array 的情况
+ let child;
+ let nextName;
+ let subtreeCount = 0; // 找到的 children 的数量
+ // 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(
+ child,
+ nextName,
+ callback,
+ traverseContext,
+ );
+ }
+ } else {
+ // 如果不是数组,但是有迭代器, 表示可遍历,
+ const iteratorFn = getIteratorFn(children);
+ if (typeof iteratorFn === 'function') {
+ 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,
+ );
+ }
+ }
+ }
+
+ return subtreeCount;
+ }
+ /**
+ * @param {obj} bookKeeping traverseContext 前面从 context 池子拿出来转换过的 context 携带着一些信息
+ * @param {*} child props.children
+ * @param {*} childKey
+ */
+function mapSingleChildIntoContext(bookKeeping, child, childKey) {
+ const {result, keyPrefix, func, context} = bookKeeping;
+ // 调用我们最开始自定义的 mapFunction,并拿到返回结果, 这里用到了 count
+ let mappedChild = func.call(context, child, bookKeeping.count++);
+
+ // 有可能我们自己返回的时候,返回的是数组,那么就继续回到 mapIntoWithKeyPrefixInternal 中
+ if (Array.isArray(mappedChild)) {
+ mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
+ } else if (mappedChild != null) {
+ // 如果是可用的 element, 那么 clone 一下,就像 Array.prototype.map 返回的是一个新的数组一样
+ 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,
+ );
+ }
+ // 将结果 push 到结果数组中去
+ result.push(mappedChild);
+ }
+ }
+
export {
mapChildren as map,
};
\ No newline at end of file
--
Gitee
From 7ae1dcd94459ac77a262ba872273aa2ef852cca1 Mon Sep 17 00:00:00 2001
From: linlin <1259649692@qq.com>
Date: Thu, 9 Apr 2020 10:15:45 +0800
Subject: [PATCH 2/2] handmapchildren
---
src/react/ReactChildren.js | 246 ++++++++-----------------------------
1 file changed, 49 insertions(+), 197 deletions(-)
diff --git a/src/react/ReactChildren.js b/src/react/ReactChildren.js
index 5de2f60..7da511b 100644
--- a/src/react/ReactChildren.js
+++ b/src/react/ReactChildren.js
@@ -1,208 +1,60 @@
-//这个方法接受3个参数children、func和context。
-//children就是将要被遍历的子组件数组,func是对单个子组件需要执行的函数,context则是func执行时this指针所指向的对象。
-function mapChildren(children, func, context) {
- //TODO实现此mapChildren方法
- if (children == null) {
- return children;
- }
+//react.map.children
+import { REACT_ELEMENT_TYPE } from '../shared/ReactSymbols';
+/* function mapChildren(children, mapFunction, context) {//children = A {$$typeof:REACT_ELEMENT_TYPE}
+ return children.flat(Infinity).map(mapFunction).flat(Infinity);
+} */
+const SEPARATOR = '.';//分隔符 开头的分隔符
+const SUB_SEPARATOR = ':';//子分隔符 中间件分隔符
+function mapChildren(children, mapFunction, context) {//children = A {$$typeof:REACT_ELEMENT_TYPE}
const result = [];
- mapIntoWithKeyPrefixInternal(children,result,null,func,context);
- return result
-}
-
-/**
- * @param {*} props.children
- * @param {Array} array 结果数组
- * @param {*} prefix null
- * @param {function} func 每个孩子调用的 callback
- * @param {obj} context func调用的上下文
- */
-function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
- // 和 prefix 前缀相关的可以暂时不用管
- /*let escapedPrefix = '';
- if (prefix != null) {
- escapedPrefix = escapeUserProvidedKey(prefix) + '/';
- }*/
- // 从 存储 context 池子中拿取一个空对象来用
- const traverseContext = getPooledTraverseContext(
- array,
- escapedPrefix,
- func,
- context,
- );
- traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
- releaseTraverseContext(traverseContext);
- }
- // context 池子的大小
-const POOL_SIZE = 10;
-// context 池
-const traverseContextPool = [];
-/**
- * 从 context 池子中拿到一个空的 context 对象来用,然后将传进去的参数添加到 context 中,
- * 并添加 count,初始值为 0,用来记录这个遍历过程中每一个被遍历到的 child 的顺序, 并被当做 mapfunction 的第三个 index 参数。然后返回 context
- * @param {array} mapResult 结果数组
- * @param {string} keyPrefix 前缀
- * @param {funcgtion} mapFunction 每一个孩子调用的 callback
- * @param {obj} mapContext mapFunction 调用的时候的上下文
- */
-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,
- };
- }
-}
-
-// 释放使用过的 context,将参数置为初始值,如果线程池没有满,那么就讲这个
-// 使用过的 context 添加进去。这样做的目的是为了防止频繁的分配内存,影响性能。
-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);
- }
+ mapIntoWithKeyPrefixInternal(children, result, null, mapFunction, context);
+ return result;
}
-/**
- * 遍历 children 实现
- * @param {?*} props.children
- * @param {!string} nameSoFar Name of the key path so far.
- * @param {!function} callback 对每个找到的 children 调用的方法,在它的内部会调用我们使用的时候传入的那个 mapFunction,然后把结果 push 到 result 数组中。
- * @param {?*} traverseContext 用于在遍历过程中传递信息。
- * @return {!number} 返回当前参数 children 下有多少个孩子
- */
-function traverseAllChildrenImpl(
- children,
- nameSoFar,
- callback,
- traverseContext,
- ) {
- // -------------------------- 首先处理 单个 children 的情况
- const type = typeof children;
-
- if (type === 'undefined' || type === 'boolean') {
- children = null;
- }
-
- let invokeCallback = false;
- // 为 null 也会调用
- if (children === null) {
- invokeCallback = true;
- } else {
- // 单个节点可能存在下面几种情况
- switch (type) {
- case 'string':
- case 'number':
- invokeCallback = true;
- break;
- case 'object':
- switch (children.$$typeof) {
- case REACT_ELEMENT_TYPE:
- case REACT_PORTAL_TYPE:
- invokeCallback = true;
- }
- }
- }
- // 如果 children 是单个节点
- if (invokeCallback) {
- callback(
- traverseContext,
- children,
- nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
- );
- // 只有一个节点 那么 children 数量就是 1, 该函数返回 children 的数量,所以这里直接返回 1
- return 1;
+//prefix指的是渲染前的节点key 最终key的/前面那部分
+function mapIntoWithKeyPrefixInternal(children, result, prefix, mapFunction, context) {
+ //traverseContext 遍历的上下文
+ if (prefix !== null) {
+ prefix = prefix + '/'; // .0:0 => .0:0/
}
- // -------------------------- 处理children 是 Array 的情况
- let child;
- let nextName;
- let subtreeCount = 0; // 找到的 children 的数量
- // 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(
- child,
- nextName,
- callback,
- traverseContext,
+ const traverseContext = { result, prefix, mapFunction, context };
+ traverseAllChildren(children, '', mapSingleChildIntoContext, traverseContext);
+}
+//A .0:0
+function traverseAllChildren(children, nameSoFar, mapSingleChildIntoContext, traverseContext) {
+ let type = typeof children;
+ //如果type是字符串或者数字,或者type是一个对象,但是children.$$typeof是一个React元素,说明children是 一个可渲染的节点
+ if (type === 'string' || type === 'number' || (type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)) {
+ mapSingleChildIntoContext(traverseContext, children,
+ nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
);
- }
- } else {
- // 如果不是数组,但是有迭代器, 表示可遍历,
- const iteratorFn = getIteratorFn(children);
- if (typeof iteratorFn === 'function') {
- 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 (Array.isArray(children)) {
+ //如果传过来的nameSoFar是空的.前缀就是.,否则就是:
+ //第二次进来的时候 nameSoFar = .0 nextNamePrefix=:
+ let nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUB_SEPARATOR;
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];//[A, B] .0
+ let nextName = nextNamePrefix + getComponentKey(child, i);// .0:0
+ traverseAllChildren(child, nextName, mapSingleChildIntoContext, traverseContext);
}
- }
}
-
- return subtreeCount;
- }
- /**
- * @param {obj} bookKeeping traverseContext 前面从 context 池子拿出来转换过的 context 携带着一些信息
- * @param {*} child props.children
- * @param {*} childKey
- */
-function mapSingleChildIntoContext(bookKeeping, child, childKey) {
- const {result, keyPrefix, func, context} = bookKeeping;
- // 调用我们最开始自定义的 mapFunction,并拿到返回结果, 这里用到了 count
- let mappedChild = func.call(context, child, bookKeeping.count++);
-
- // 有可能我们自己返回的时候,返回的是数组,那么就继续回到 mapIntoWithKeyPrefixInternal 中
+}
+function getComponentKey(component, index) {
+ return component.key || index.toString(36);//如果说此节点有自己的key,就用自己的key,如果没有就用它的索引
+}
+//如果执行到这个地方 child肯定是一个节点 child=A childKey =.0:0
+function mapSingleChildIntoContext(traverseContext, child, childKey) {//
+ let { result, prefix, mapFunction, context } = traverseContext;
+ //let mappedChild = mapFunction(child);
+ let mappedChild = mapFunction.call(context, child);//child = A {$$typeof:REACT_ELEMENT_TYPE}
+ //mappedChild===child
+ //mappedChild = [item, [item, [item, [item]]]]; 往result里面放的永远只能是一个对象,不能是数组
if (Array.isArray(mappedChild)) {
- mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
- } else if (mappedChild != null) {
- // 如果是可用的 element, 那么 clone 一下,就像 Array.prototype.map 返回的是一个新的数组一样
- 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,
- );
- }
- // 将结果 push 到结果数组中去
- result.push(mappedChild);
+ mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c, context);
+ } else {
+ //把这个对象展开,重写key prefix转换前的索引组成的key/childKey转换后的索引组件的key
+ result.push({ ...mappedChild, key: prefix + childKey }); // .0:0/
}
- }
+}
export {
mapChildren as map,
--
Gitee