React 16.2.0 core部分模块解析。

React16版本中的对象包括Children, Component, PureComponent, unstable_AsyncComponent(还在测试中), Fragment, 以及从前就有的createElement, cloneElement, createFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const React = {
Children: {
map,
forEach,
count,
toArray,
only,
},
Component,
PureComponent,
unstable_AsyncComponent: AsyncComponent,
Fragment: REACT_FRAGMENT_TYPE,
createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,
version: ReactVersion,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactCurrentOwner,
// Used by renderers to avoid bundling object-assign twice in UMD bundles:
assign,
},
};

先来看看Children

ReactChildren

这个模块中封装了了一些遍历子节点的方法,其中最重要的方法是

1
2
3
4
5
traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext)

这个方法用迭代的方式来遍历子节点,子节点以数组嵌套或者迭代器嵌套的方式存储,该方法所要做的是根据数组或者迭代器来访问到以入参children为起始点的所有节点,节点指的是类型为ReactElement(Symbol)、数字(Symbol的替代形式)、字符串或者null的节点,在此节点上调用传入的callback方法,同时向callback方法中传入tranverseContext对象,当前遍历到的children,以及计算出的节点名称。

节点名称的计算公式为:’.’ + getComponentKey + ‘:’ + getComponentKey + ‘:’ + …
其中getComponentKey方法会返回component手动设置的key或者遍历到当前节点的index

tranverseContext是个可以设置的对象,包含以下五项:result, keyPrefix, func, context, count。其中result是一个数组,每到遍历到一个对象时,若有返回结果则会返回到此数组中,keyPrefix是个自定义的主键前缀,func会在遍历时就执行,执行的上下文为context,count用来记录当前遍历到的节点数量。

于是这个模块暴露出五个方法:

  • forEachChildren(children, forEachFunc, forEachContext),其中forEachFunc对应tranverseContext的func,forEachContext对应context,traverseAllChildrenImpl中的callback的调用为:forEachFunc.call(forEachContext, child, count),count为当前遍历到的次序值。

  • mapChildren(children, func, context),其中func对应tranverseContext的func,context对应context,不同的在于在traverseAllChildrenImpl中的callback与traverseContext的设置。traverseContext中传入一个空数组来记录mapChildren函数的返回值,而且传入的func需具有返回值,有效的返回值为ReactElement对象或者包含ReactElement的数组,调用与之前一致 mappedElement = func.call(context, child, count),count为当前遍历到的次序值。

  • countChildren(children, context),只是计数而已,调用和之前一致

  • toArray(children),将树形结构整理成数组形式,实现相当于简化的mapChildren

  • onlyChild(children),只是判断传入的是不是个ReactElement

可以引入一张我自己画的图来帮助理解:

再来看看ReactElement

ReactElement

Element和Component不是一回事,Element就是包含以下这几个属性的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
const element = {
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};

而createElement(type, config, children)就是一个通过输入的config来配置当前的ReactElement的工厂方法。一般是config中有什么就替换掉默认配置项,并赋值到props中,其中保留字[key, refs, self, source]不能被赋值,type对应的是element中的type,其中children在工厂方法中被赋值到props.children中去,并且无法直接更改(Object.freeze)。

再来看看Component

Component

Component具有四个自己的对象属性:updater由ReactDOM来实现

1
2
3
4
5
6
7
8
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

Component的原型中具有两个方法,setState和forceUpdate,均与updater的实现有关,

1
2
3
4
5
6
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

而PureComponent与AsyncComponent只是有一个静态属性的区别

1
2
pureComponentPrototype.isPureReactComponent = true;
asyncComponentPrototype.unstable_isAsyncReactComponent = true;