completeWork 阶段
performUnitOfWork
的第二步就是completeWork
阶段,每次都会执行completeUnitOfWork
方法:
function performUnitOfWork(unitOfWork: Fiber): void {
// ...
if (next === null) {
// 如果没有 next child,那么开始 complete.
// 创建可真实节点
completeUnitOfWork(unitOfWork);
} else {
// 如果存在 next,说明还有 child,继续向下递归 beginWork
workInProgress = next;
}
}
bubbleProperties
对于所有的fiber
都会进行bubbleProperties
处理。该函数的作用是遍历workInProgress
的第一层子节点,将所有child.lanes
和child.childLanes
合并到当前的childLanes
上。将所有child.subtreeFlags
和child.flags
合并到当前subtreeFlags
上。
HostComponent
相较于其他节点,HostComponent
节点比较特殊。
// 将当前 fiber 移出
popHostContext(workInProgress);
// 获取的是当前的 RootHostContainer
const rootContainerInstance = getRootHostContainer();
首先会移除当前的HostContext
,其次是获取container
节点。为什么能获取到真实节点呢?这是因为在beginWork
中对于HostRoot
和HostPortal
节点都会将真实节点container
全局存储到rootInstanceStackCursor.current
,而在completeWork
阶段将其移出。在访问子节点时就能正确获取到它所在的容器了。
真实节点可复用时
接下来就是渲染真实节点的过程:
if (current !== null && workInProgress.stateNode != null) {
// 如果真实节点存在,那么进行更新
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
}
如果老fiber
存在且stateNode
存在,说明已经复用了fiber
且具备真实节点。这个时候只需要更新属性即可:
updateHostComponent = function (//...) {
// ...
// 类似结构:['name', '张三', 'id', 333, 'style', { color: 'red' }]
const updatePayload = prepareUpdate(
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
workInProgress.updateQueue = (updatePayload: any);
// 标记为 Update
if (updatePayload) {
markUpdate(workInProgress);
}
};
这个更新的过程正如UpdateQueue
章节中提到的,主要是通过prepareUpdate
方法对比节点的新旧props
,最后将改变了的属性记录成数组形式。其中偶数index
为键,奇数index
为值。结构类似如下:
['name', '张三', 'id', 333, 'style', { color: 'red' }]
真实节点不可复用时
第二种情况是current
不存在或者是stateNode
不存在,那么真实节点就无法复用了,需要重新创建:
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
export function createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): Instance {
// 创建了 element
const domElement: Instance = createElement(
type,
props,
rootContainerInstance,
parentNamespace,
);
// 建立关系 node . '__reactFiber$' + randomKey = fiber
precacheFiberNode(internalInstanceHandle, domElement);
// 建立关系 node . '__reactProps$' + randomKey = props
updateFiberProps(domElement, props);
return domElement;
}
createInstance
方法会创建一个真实节点,并且建立真实节点与fiber
和props
的关系。随后添加所有子节点:
appendAllChildren(instance, workInProgress, false, false);
// 如果是普通节点的话,会将 stateNode 存起来
// 与 fiber 建立了联系,真实节点存放在 stateNode 上
workInProgress.stateNode = instance
appendAllChildren
方法会将能渲染的子节点全部添加到当前创建的节点instance
上,这样依次向上进行completeWork
时,就会形成一棵具有真实节点树。