src\index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { mapChildren } from './react/ReactChildren';
class Child extends Component {
render() {
console.log('this.props.children', this.props.children);
const mappedChildren = mapChildren(this.props.children, (c, index) => [<div key={`div${index}A`}>{c}</div>, <div key={`div${index}B`}>{c}</div>])
console.log('mappedChildren', mappedChildren)
return <div>{mappedChildren}</div>;
}
}
class App extends Component {
render() {
return (
<Child>
<div>
child1
</div>
<div key="key2">child2</div>
<div key="key3">child3</div>
{[
<div key="key4">child4</div>,
<div key="key5=">child5</div>,
<div key="key6:">child6</div>,
]}
</Child>
)
}
}
ReactDOM.render(
<App />
, document.getElementById('root'));
react\ReactElement.js
import ReactCurrentOwner from './ReactCurrentOwner';
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true
};
function hasValidRef(config) {
return config.ref !== undefined;
}
function hasValidKey(config) {
return config.key !== undefined;
}
export function createElement(type, config, children) {
let propName;
//保留的属性被移除掉了
const props = {};
let key = null;
let ref = null;
let self = null;//获取this指向
let source = null;//获取行等信息
// 判断是否传入配置
if (config != null) {
// 验证 ref 和 key
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
// __self 是为了以后正确获取 this
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 遍历配置,把非保留属性拷贝到props上去
for (propName in config) {
if (!RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
}
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 判断是否有给组件设置 defaultProps,有的话判断是否有给 props 赋值,只有当值为undefined 时,才会设置默认值
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
//这是一个工厂函数,帮助我们创建React元素
const ReactElement = function (type, key, ref, self, source, owner, props) {
const element = {
//这个标志让我们可以唯一的标识这是一个React元素
$$typeof: REACT_ELEMENT_TYPE,
//此元素的内建属性
type: type,
key: key,
ref: ref,
props: props,
//记录一下创建此元素组件
_owner: owner,
};
return element;
};
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
export function cloneAndReplaceKey(oldElement, newKey) {
const newElement = ReactElement(
oldElement.type,
newKey,
oldElement.ref,
oldElement._self,
oldElement._source,
oldElement._owner,
oldElement.props,
);
return newElement;
}
react\ReactCurrentOwner.js
const ReactCurrentOwner = {
current: null
};
export default ReactCurrentOwner;
src\react\ReactBaseClasses.js
const emptyObject = {};
/**
* 基本帮助类用来更新组件的状态
*/
function Component(props, context, updater) {
this.props = props;//属性对象
this.context = context;//上下文对象
//如果组件有字符串refs,我们将在随后赋值为一个不同的对象
this.refs = emptyObject;
//我们初始化了默认的updater,但是真正的updater是renderer注入的那一个
this.updater = updater;
}
Component.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.isReactComponent = {};
function ComponentDummy() { }
ComponentDummy.prototype = Component.prototype;
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
export { Component, PureComponent };
src\shared\ReactSymbols.js
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
export const REACT_ELEMENT_TYPE = hasSymbol
? Symbol.for('react.element')
: 0xeac7;
export const REACT_FORWARD_REF_TYPE = hasSymbol
? Symbol.for('react.forward_ref')
: 0xead0;
src\react\ReactChildren.js
import { isValidElement, cloneAndReplaceKey } from './ReactElement';
import { REACT_ELEMENT_TYPE } from '../shared/ReactSymbols';
const POOL_SIZE = 10;
const traverseContextPool = [];
const SEPARATOR = '.';
const SUBSEPARATOR = ':';
const userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}
/**
* 因为=和:有特殊含义,所以需要转义
* =替换成=0
* :替换成=2 因为:是分隔符
* @param {*} key
*/
function escape(key) {
const escapeRegex = /[=:]/g;
const escaperLookup = {
'=': '=0',
':': '=2',
};
const escapedString = ('' + key).replace(escapeRegex, function (match) {
return escaperLookup[match];
});
// key前面要加$
return '$' + escapedString;
}
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,//计算 children 的个数,计算的是摊平后数组元素的个数
};
}
}
/**
* 映射子元素,一般是props.children
* 提供的函数将会被通过传递(child,key,index)来为每一个子节点调用
* @param {*} children 要迭代的子元素数组
* @param {*} func 映射函数
* @param {*} context map函数的上下文对象
* @returns 包含结果的数组
*/
function mapChildren(children, func, context) {
debugger;
if (children == null) {
return children;
}
const result = [];// 遍历出来的元素会丢到result中最后返回出去
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
//将 children 完全遍历,遍历的节点最终全部存到 array 中,是 ReactElement 的节点会更改 key 之后再放到 array 中
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);
}
/**
* 将 child 推入 traverseContext 的 result 数组中,child 如果是 ReactElement,则更改 key 了再推入
* 只有当传入的 child 是可渲染节点才会调用。如果执行了 mapFunc 返回的是一个数组,则会将数组放到 mapIntoWithKeyPrefixInternal 继续处理
* @param {*} bookKeeping 就是我们从对象池子里取出来的东西,`traverseContext`
* @param {*} child 传入的节点,`children`
* @param {*} childKey 节点的 key,`nameSoFar`
*/
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const { result, keyPrefix, func, context } = bookKeeping;
// func 就是我们在 React.Children.map(this.props.children, c => c)中传入的第二个函数参数
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,
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);
}
}
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);
}
}
/**
* 遍历子节点,比如props.children
* traverseContext是一个可选参数,会被在整个遍历过程中被传递。它可能被用来进行仓库累加或者任何其它
* 在回调中可以找到关联的地方
*
* @param {?*} children 要遍历的子节点
* @param {!function} callback 在每一个子节点上被调用
* @param {?*} traverseContext 遍历时候的上下文
* @return {!number} 在子树中的子节点的数量
*/
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
/**
* 获取组件的key
* @param {*} component React元素
* @param {*} index 索引
*/
function getComponentKey(component, index) {
if (//如果是对称并且有key的话就返回转义后的key
typeof component === 'object' &&
component !== null &&
component.key != null
) {
return escape(component.key);
}
return index.toString(36);
}
/**
* 如果children是可渲染节点,则调用 mapSingleChildIntoContext 把 children 推入 result 数组中
* 如果children 是数组,则再次对数组中的每个元素调用 traverseAllChildrenImpl,传入的 key 是最新拼接好的
* children 是对象,则通过 children[Symbol.iterator] 获取到对象的迭代器 iterator, 将迭代的结果放到 traverseAllChildrenImpl 处理
* 函数核心作用就是通过把传入的 children 数组通过遍历摊平成单个节点,然后去执行 mapSingleChildIntoContext
* @param {?*} children 要循环的子节点
* @param {!string} nameSoFar 父级 key,会一层一层拼接传递,用:分隔
* @param {!function} callback 如果当前层级是可渲染节点,undefined、boolean 会变成 null,string、number、$$typeof 是 REACT_ELEMENT_TYPE 或者 REACT_PORTAL_TYPE,会调用 mapSingleChildIntoContext 处理
* @param {?*} traverseContext 对象池中拿出来的一个对象,用来在遍历的过程中传递信息
* @return {!number} 在这个子树中子节点的数量
*/
function traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
const type = typeof children;
//所有的undefined或者boolean被处理为null
if (type === 'undefined' || type === 'boolean') {
children = null;
}
let invokeCallback = false;
if (children === null) {
invokeCallback = true;
} else {
switch (type) {
case 'string':
case 'number':
invokeCallback = true;//如果是字符串或者数字则可以直接回调
break;
case 'object':
switch (children.$$typeof) {//如果是树象,并且是一个React元素节点可以直接回调
case REACT_ELEMENT_TYPE:
invokeCallback = true;
}
}
}
if (invokeCallback) {//如果可以调用回调就则直接调用
callback(
traverseContext,
children,
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}
let child;
let nextName;
let subtreeCount = 0;//在当前的子树中查找到的子节点的数量
const nextNamePrefix =
nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;//如果nameSoFar为空就是.,否则就是nameSoFar:
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);// 获得下一个名称 .0 或者.$key
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
}
return subtreeCount;
}
export {
mapChildren
}