在目前更新props的逻辑中,我们发现,如果更新一个子组件的话,会导致整个dom树都会更新,这样就造成了性能的浪费,所以我们只需要更新变化的子组件即可。
更新逻辑
旧的逻辑:
在 update()
函数中,是将根节点重新赋值给nextUnitOfFier
后重新启动 perfromFiberOfUnit
,创建dom,建立链表关系,对新旧dom树的props进行更新、删除、新建操作,最后重新挂载到根节点
优化后的逻辑:
在 update()
函数中,将需要更新的组件赋值给nextUnitOfFier
后重新启动后面的逻辑,并且在下一个组件更新前终止。
按照优化逻辑,我们需要获取到开始点和结束点:
- 开始点:当前更新的组件
- 结束点:当前更新组件的sibling节点
代码实现
获取起始点
重新创建一个wipFiber参数用来表示当前更新的组件fiber
js
let wipFiber = null
function updateFunctionComponent(fiber) {
// 获取正在更新的组件
wipFiber = fiber;
const children = [fiber.type(fiber.props)];
reconcileChildren(fiber, children);
}
但由于wipFiber是一个全局变量,会导致后面的组件参数覆盖之前的组件参数,所以可以采用闭包的方式来存下当前组件fiber。
将这个currentFiber赋值给wipRoot后,我们就获取到了开始点,然后赋值给nextUnitOfFier后启动更新。
js
function update() {
let currentFiber = wipFiber;
return () => {
wipRoot = {
...currentFiber,
alternate: currentFiber,
};
nextUnitOfFier = wipRoot;
};
}
获取结束点
在更新逻辑中,如果碰到结束点的话,就应该停止创建链表,将已更新的dom挂载到对应的节点上去,所以结束点应该在 workLoop
逻辑中,
当判断当前更新的fiber(wipRoot)的sibling与 perfromFiberOfUnit
返回的 nextUnitOfFier
的type相同则表示结束点
js
function workLoop(idleDeadline) {
let shouldYield = false;
while (!shouldYield && nextUnitOfFier) {
nextUnitOfFier = perfromFiberOfUnit(nextUnitOfFier);
// 判断结束点
if (wipRoot?.sibling?.type === nextUnitOfFier?.type) {
nextUnitOfFier = undefined;
}
shouldYield = idleDeadline.timeRemaining() < 1;
}
// ...
}
给nextUnitOfFier赋值为undefined后结束更新vdom,开启挂载操作。