本文较长,请耐心读完,相信我定有收货,如果有错误欢迎指正。
reconcileChildren
这个方法做的事情是对于刚创建的组件,会创建新的子 Fiber
节点,update
组件,将当前组件与该组件在上次更新时对应的 Fiber
节点比较(也就是俗称的 Diff
算法),将比较的结果生成新 Fiber
节点。
function reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime) {
// 首次渲染时只有root节点存在current,所以只有root会进入reconcile产生effectTag
// 其他节点会appendAllChildren形成DOM树
if (current === null) {
workInProgress.child = mountChildFibers(
workInProgress, null, nextChildren, renderExpirationTime
);
} else {
workInProgress.child = reconcileChildFibers(
workInProgress, current.child, nextChildren, renderExpirationTime
);
}
}
通过调试,current
首次渲染只有 root
上有值,其他节点为 null
,等状态更新时候所有的 current
都会存在了,可以看到传入的参数还有一个 workInProgress
,这个就是 react
中用到的双缓冲技术。
current
代表当前展示视图对应的 fiber
树,workInProgress
代表正在构建中的树,通过 alternate
连接。
workInProgress
构建完成后根节点又通过改变 current
指向 workInProgress
,所以 wip
又变回了 current
树,其中在构建 wip
树的时候会选择性的复用 current
树节点。
另外通过初始渲染和更新渲染后都返回了 workInProgress.child
作为下个时间分片任务单元。
一、ChildReconciler总览
最上面的代码中有两个分支,初始加载和更新时,调用不同的方法,但是最终都是执行,通过参数区分:
var reconcileChildFibers = ChildReconciler(true);
var mountChildFibers = ChildReconciler(false);
但是这个方法 中做了很多的事情,大致看下结构:
function ChildReconciler(shouldTrackSideEffects) {
function deleteChild(returnFiber, childToDelete) {
}
function deleteRemainingChildren(returnFiber, currentFirstChild) {
}
function mapRemainingChildren(...) {
}
function useFiber(...) {
}
function placeChild(...) {
}
function placeSingleChild(...) {
}
function updateTextNode(...) {
}
function updateElement(...) {
}
function updatePortal(...) {
}
function updateFragment(...) {
}
function createChild(...) {
}
function updateSlot(...) {
}
function updateFromMap(...) {
}
function warnOnInvalidKey(...) {
}
function reconcileChildrenArray(...) {
}
function reconcileChildrenIterator(...) {
}
function reconcileSingleTextNode(...) {
}
function reconcileSingleElement(...) {
}
function reconcileSinglePortal(...) {
}
function reconcileChildFibers(...) {
}
return reconcileChildFibers;
}
乍一看这个方法无从下手,从命名来看有插入、更新、删除、调和等关键信息,最后暴露出的是 reconcileChildFibers
方法,所以先从这里入手,大致了解其中做的事情。
二、reconcileChildFibers
function reconcileChildFibers(...): Fiber | null {
// fragments <>{[...]}</> and <>...</>. 所以经常使用的fragments优化的点体现在这里,直接拿子节点
...
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
// Handle object types
const isObject = typeof newChild === 'object' && newChild !== null;
//对象的形式
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(...),
);
case REACT_PORTAL_TYPE:
...
}
}
//return 1 单个
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(...),
);
}
//return [1,2,3] 节点中有并级 现在的业务中这里是一般必然要走的
if (isArray(newChild