ReactCompositeComponent

简述

ReactCompositeComponent在整个环节中是负责自定义组件的处理。

当我们写下一个div、span标签的时候react使用ReactDomComponent来处理它,而当我们定义了一个Bar、Foo标签的时候React则会使用ReactCompositeComponent来处理它。

而应该说,当我们写下一个业务时候,大多数时候都是在使用自定义组件来实现它们,所以React源码模块中如果有个模块是不得不读,那么肯定就是它了。

API

在说API之前有句话不得不说,当我想分析ReactCompositeComponent的时候,其实我想的其实是ReactCompositeComponentWrapper,它基本是ReactCompositeComponent的实例的一个小扩展。

不过在开始之前 还是先谈谈ReactCompositeComponent的API。过滤内部私有的方法和属性,大致如此:

方法说明
construct构造器,实例化时候需要先执行这个函数
mountComponent初始化组件(_constructComponent),渲染markup(performInitialMount)并注册事件
_constructComponent引用_constructComponentWithoutOwner初始化组件。 被mountComponent引用
_constructComponentWithoutOwner初始化组件 被_constructComponent引用
performInitialMountWithErrorHandling渲染markup 被mountComponent引用
performInitialMount渲染markup 被mountComponent引用
getHostNode获取组件对应的HTMLElement,这里主要是_updateRenderedComponent用到
unmountComponent调用组件自身的unmountComponent并清空相关数据
_maskContext被_processContext引用
_processContext过滤有效contextTypes
_processChildContext将子元素context和当前context合并并返回
_checkContextTypes开发模式下辅助函数 检查contextTypes
receiveComponent归集收到的新旧元素 调用updateComponent进行更新
performUpdateIfNecessary传入事务对象,对事务过程中影响的元素进行更新
updateComponent满足更新条件后调用_performComponentUpdate进行更新
_processPendingState
_performComponentUpdate调用_updateRenderedComponent进行组件更新
_updateRenderedComponent调用组件自身的render进行更新(或者直接调用_replaceNodeWithMarkup替换),并触发当前组件生命周期
_replaceNodeWithMarkup使用markup直接替换旧的组件
attachRef为组件设置refs引用 ref内部属性指向组件的HTMLElement
detachRef移除refs引用
getName获取组件的名称 例如TopLevelWrapper
getPublicInstance获取component instance,createElement返回的那种
_instantiateReactComponentnull

内部调用

这里自己调用自己方法比较多,简单捋一捋

1
2
3
4
5
6
7
8
9
10
11
1.mountComponent
-> _constructComponent(初始化组件) && performInitialMount(渲染markup)
|->_constructComponentWithoutOwner |-> _renderValidatedComponent

2._processContext
-> _maskContext

3.updateComponent
-> _performComponentUpdate
-> _updateRenderedComponent
-> component.render || _replaceNodeWithMarkup

API

construct

主要为实例是挂载以下属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
this._currentElement = element;
this._rootNodeID = 0;
this._compositeType = null;
this._instance = null;
this._hostParent = null;
this._hostContainerInfo = null;
this._updateBatchNumber = null;
this._pendingElement = null;
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
this._renderedNodeType = null;
this._renderedComponent = null;
this._context = null;
this._mountOrder = 0;
this._topLevelWrapper = null;
this._pendingCallbacks = null;
this._calledComponentWillUnmount = false;

_constructComponentWithoutOwner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var Component = this._currentElement.type;

if (doConstruct) {
if (__DEV__) {
// 略
} else {
return new Component(publicProps, publicContext, updateQueue);
}
}

// This can still be an instance in case of factory components
// but we'll count this as time spent rendering as the more common case.
if (__DEV__) {
// 略
} else {
return Component(publicProps, publicContext, updateQueue);
}

这里type是一个函数, 实际上它就是我们写的组件、页面这类。这块可以参考jsx到js的转换这块。

他最终返回的是一个React.createElement()返回值。

performInitialMount

这个函数里面执行了mount之前的一些生命周期函数。

关键代码是这样的,实在是不得不提。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent();
}

var nodeType = ReactNodeTypes.getType(renderedElement);
this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(
renderedElement,
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */,
);
this._renderedComponent = child;

var markup = ReactReconciler.mountComponent(
child,
transaction,
hostParent,
hostContainerInfo,
this._processChildContext(context),
debugID,
);
  • _renderValidatedComponent是内部的一个私有的安全的render调用,调用组件自身的render函数返回结果。

  • _instantiateReactComponent则实际上是在ReactCompositeComponentWrapper上定义的。关于ReactCompositeComponentWrapper实际上instantiateReactComponent中有过定义,它会根据renderedElement.type情况返回四种实例中等之一,这里的child是自定义元素内部的子元素,所以类型,这里没毛病。

  • ReactReconciler.mountComponent也有过分析,它实质上只负责找到child对应的模块类型,然后调用其自身的mountComponent进行挂载。

unmountComponent

这里做了几件事

  • 生命周期componentWillUnmount函数激活
  • 将construct函数挂载上去的东西清空
  • 调用组件自身unmountComponent来卸载
  • ReactInstanceMap.remove(inst) 移除内部对此实例的内部引用。

updateComponent

这里有一个调用链,可以简单回顾一下。

这里的主要逻辑是

  • 归集prevProps、nextProps、nextState、nextConext判断是否进行更新
  • 如果更新 调用_performComponentUpdate来进行更新
  • 如果不更新将上面收集到props、state、content赋值给nextElement并更新_currentElement引用

_performComponentUpdate

它也不是更新的实质性执行者。它做了以下的事情

  • 处理好componentWillUpdate&componentDidUpdate生命周期函数的调用
  • 调用_updateRenderedComponent执行实质性更新

_updateRenderedComponent

这个函数呢有两个分支。

它会做这样的处理。

  • 如果是同一个组件 则需要更新 => 就执行组件的receiveComponent来呼起自身的更新逻辑
  • 如果不是同一个组件则需要进行替换 => 这是先卸载原组件 然后挂载新组件