Reconciler范畴
关注点放到src/renders/shared/stack/reconciler
上来。这里Reconciler模块的实现代码。
1 | renderers/shared/stack/reconciler |
getHostComponentFromComposite
用于从自定义组件上获取实例上的_renderedComponent
,如果自定义组件不断相互嵌套使用,他还会不断往下深挖,直到返回的是ReactDomComponent
节点为止,如果为空返回null。
这个函数可以反馈组件架构上的涉及:
_renderedComponent
保存children节点_renderedNodeType
保存节点类型1
2
3
4
5Enum ReactNodeTypes {
HOST: 0,
COMPOSITE: 1,
EMPTY: 2,
}
instantiateReactComponent
根据给出的ReactNode返回一个组件实例。
小小研究一下这个函数,其实可以窥见ReactNode和React组件实例之间的关系。
1 | type ReactDOMElement = { |
对于ReactDOMElement
来说,type是字符串
对于ReactComponentElement
来说, type是一个函数
这里这个函数根据type属性做了4个分支
- 如果是string,使用ReactHostComponent.createInternalComponent(element)返回ReactDOMElement
- 如果是function且已知(定义好了mountComponent|receiveComponent这些),那么调用type(自定义组件的render函数)
- 如果是function且未知,使用
new ReactCompositeComponentWrapper(element)
返回一个ReactCompositeComponent包装结构。 - 如果这个节点没有type属性直接是一个string|number,那么返回一个文本节点
ReactChildReconciler
这个函数是Diff算法的一部分。主要是Element Diff部分。
1 | type ReactChildReconciler { |
这里updateChildren是diff算法的核心实现。
ReactComponentEnvironment
代码相关引用路径如下:
1 | var ReactComponentEnvironment = { |
这里为Reconciler模块抽象出了需要了解浏览器上下文的功能。这些都是一些实质性对浏览器DOM进行操作的函数。
这里先说processChildrenUpdates
,它主要引用了DOMChildrenOperations.processUpdates
,关于这个函数,参考Diff算法-更新到DOM。因为这里是Diff操作后,将虚拟dom变化反馈到实质Dom的流程,所以必须按照浏览器的DOM API来进行dom更新,这里必须知道浏览器上下文。
其次是replaceNodeWithMarkup
,它指向了DOMChildrenOperations.DOMChildrenOperations
。这个函数用途也是在Diff环节中的,我们知道,如果一个组件没有变化只是props更新,那么它会走更新逻辑,但是变化了呢?它会直接删除旧的+替换为成新生成的,这就是这个API的功能。这里替换都是用的replaceChild,暂时也没看到和浏览器兼容相关代码,此处兼容相关的是insertTreeChildren
。
ReactCompositeComponent
这个是自定义组件的相关代码,这里不再说细节。
主要是提一下,自定义组件也是Reconciler模块的一部分。
ReactDefaultBatchingStrategy
这个模块是批量更新的处理。主要涉及到3个部分: 事务、更新。
两个事务在于:
1 | var RESET_BATCHED_UPDATES = { |
前者是模块事务操作,后者是更新事务。其中模块事务完毕后要重设isBatchingUpdates=false;
这个isBatchingUpdates
变量是事务过程中需要使用的变量:当没有事务执行中的时候直接执行,否则以事务方式执行这个函数(先执行initializeAll, 函数完毕再执行closeAll)。
核心代码:
1 | var ReactDefaultBatchingStrategy = { |
更新事务则关注ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
。这里值得扯一扯的是ReactUpdates.batchedUpdates
实质上指向了这个ReactDefaultBatchingStrategy.batchedUpdates
,最后又根据事务反向指向回去了flushBatchedUpdates
。
具体细节在《React ReactUpdates》里面有抽丝剥茧式得剖析,这里不再赘述。
只是做宏观讲述,这个batchedUpdates最后是对所有脏组件(dirtyComponents),调用其自身receiveComponent
函数走Diff,然后反馈到浏览器UI。
★这里倒是可以提一提dirtyComponents的维护。直接调用是ReactUpdates.enqueueUpdate
,这里有简单的说明。
它这里有一个主要维护路径:
1 | props更新(假如有) |
这里就是脏组件的维护路径了。关于this.updater的赋值,在ReactCompositeComponent.js,line 255, mountComponent函数中,而不是setState函数里面默认的那一个。
ReactEmptyComponent
这个地方无甚可说,就是返回一个ReactDOMEmptyComponent
ReactEventEmitterMixin
这个函数作用是从委托到Document上的回调集合里面找到对应当前组件的事件,然后对事件进行分发。
关于这个部分,可以参考React Event。这里直接链入的是核心调用分析环节,如果想有一个全面了解,还是必须整体通读这一篇。
ReactHostComponent
1 | var ReactHostComponent = { |
主要就是两个函数:
createInternalComponent => new ReactDOMComponent(element)
createInstanceForText => new ReactDOMTextComponent(text)
这里需要明白,ReactNode和组件实例之间的区别。
ReactMultiChild
ReactMultiChild是Diff算法环节中重要一环。
这里因为Diff算法已经先总结好了,直接去React Diff算法看即可
ReactOwner
这个模块和ReactCompositeComponent是有很大关联性的。
这里具体参看之前的文章:ReactCurrentOwner:ReacCompositeComponent & ReactDomComponent之间的胶水。
至于除此之外的两个调用:
1 | owner.attachRef(ref, component); |
都是对ReactInstance的属性调用。这两个函数是对ref这个props的处理。
这里涉及的是ReactRef模块
ReactRef
1 | function attachRef(ref, component, owner) { |
这个ref经常用到,函数也简单到可以靠猜,所以不再赘述了。
主要提一下其调用。它们在ReactReconciler.receiveComponent
和ReactReconciler.unmountComponent
分别有相关调用。
ReactSimpleEmptyComponent
这个函数是给ReactNative用的,这里我们就不管了。
ReactUpdates
关于这个ReactUpdates,可以直接看之前的一篇, React ReactUpdates, 这里有详细的说明。
ReactUpdateQueue
ReactUpdates是一个队列的执行,但是既然是队列,那么他还是需要一个角色来维护这个队列,以便后面任务有序加入更新队列里。这里ReactUpdateQueue起到的作用就是维护这个队列。
这里还是有些细节可以看,不过这里暂时略过,后面加以补充。
体系&作用
上面主要提到了构成Reconciler体系的各个小模块。
但是这些在整个React体系里面是怎么发挥其核心作用的呢?这里需要一张图,来简要诠释,在整个React体系中,它是怎么居中调配React各个功能模块,使它井然有序工作的。