react fiber的三个阶段
- 调度阶段:根据任务的优先级、类型、过期时间来决定执行顺序
- 协调阶段:React Fiber 会遍历 Fiber 树,并根据新的 props 和 state 来计算出新的 Fiber 树,并将其存储在根节点的 Fiber 对象中。
- 提交阶段:将新的fiber树添加到真实节点中
react fiber将渲染分成多个阶段
- 异步渲染,在多个帧中分批处理。避免长时间阻塞
- 可中断,可在渲染过程中中断
- 优先级,根据优先级进行渲染
- 可预测
react fiber的执行流程
- 初始化阶段:React Fiber从根节点开始遍历整个组件树,创建Fiber节点,并为每个Fiber节点分配一个唯一的标识。
-
协调阶段:React Fiber根据优先级调度算法,决定哪些Fiber节点需要更新,哪些Fiber节点可以跳过更新。在协调阶段中,React Fiber会执行以下步骤:
a. 执行beginWork阶段:React Fiber从根节点开始,深度优先遍历整个组件树,执行每个Fiber节点的beginWork方法,生成该Fiber节点的子树。
b. 执行completeWork阶段:React Fiber从叶子节点开始,深度优先遍历整个组件树,执行每个Fiber节点的completeWork方法,生成该Fiber节点的更新。
c. 执行commit阶段:React Fiber从根节点开始,深度优先遍历整个组件树,执行每个Fiber节点的commit方法,将该Fiber节点的更新应用到DOM上。
-
渲染阶段:React Fiber将更新应用到DOM上后,会触发渲染阶段。在渲染阶段中,React Fiber会执行以下步骤:
a. 执行beginRender阶段:React Fiber从根节点开始,深度优先遍历整个组件树,执行每个Fiber节点的beginRender方法,生成该Fiber节点的子树。
b. 执行completeRender阶段:React Fiber从叶子节点开始,深度优先遍历整个组件树,执行每个Fiber节点的completeRender方法,生成该Fiber节点的渲染结果。
c. 执行commit阶段:React Fiber从根节点开始,深度优先遍历整个组件树,执行每个Fiber节点的commit方法,将该Fiber节点的渲染结果应用到DOM上。
react fiber怎么实现异步更新
React Fiber 实现异步更新的方式是使用 requestIdleCallbackAPI。这个 API 可以让浏览器在空闲时间执行回调函数,这样就可以避免阻塞主线程,提高页面的性能和响应速度。 在 React Fiber 中,每个 Fiber 节点都有一个
expirationTime属性,表示该节点的过期时间。当我们需要更新一个组件时,我们会将该组件的 Fiber 节点的expirationTime属性设置为当前时间,然后将该节点添加到一个待处理队列中。 在requestIdleCallback回调函数中,我们会遍历待处理队列,并找到下一个需要更新的 Fiber 节点。我们会检查该节点的expirationTime是否小于等于当前时间,如果是,就表示该节点已经过期,需要立即更新。否则,我们会将该节点的更新推迟到下一个空闲时间。 在更新一个 Fiber 节点时,我们会创建一个新的 Fiber 树,并将其与旧的 Fiber 树进行比较,找出需要更新的部分。然后,我们会将新的 Fiber 树转换为真实的 DOM 元素,并将其添加到页面中。
let nextUnitOfWork = null;
// 存储需要提交的Fiber节点。在更新任务执行完成后,将更新结果存放在该变量中,在下一次requestIdleCallback时,调用commitAllWork函数,提交更新
let pendingCommit = null;
function performUnitOfWork(workInProgress) {
// 执行工作单元
}
/**
* 一个循环函数,用于处理更新队列。队列为空时,结束执行
* 1、state变化时触发
* 2、render时触发
* @param {*} deadline
*/
function workLoop(deadline) {
// 执行工作循环
if (pendingCommit) {
commitAllWork(pendingCommit);
} else if (nextUnitOfWork) {
if (deadline.timeRemaining() > 0) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
workLoop(deadline);
} else {
requestIdleCallbackWithExpiration(deadline.timeRemaining());
}
}
}
function requestIdleCallbackWithExpiration(expirationTime) {
// 使用 requestIdleCallback API,设置过期时间
requestIdleCallback((deadline) => {
if (expirationTime <= deadline.timeStamp) {
if (pendingCommit) {
performSyncWork();
} else {
performAsyncWork(deadline);
}
} else {
requestIdleCallbackWithExpiration(expirationTime);
}
});
}
function scheduleWork(root, expirationTime) {
// 将 Fiber 节点添加到待处理队列中
root.expirationTime = expirationTime;
if (!nextUnitOfWork) {
nextUnitOfWork = root;
}
requestIdleCallbackWithExpiration(expirationTime);
}
function performSyncWork() {
// 同步执行更新任务
while (nextUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (pendingCommit) {
commitAllWork(pendingCommit);
}
}
function performAsyncWork(dl) {
// 异步执行更新任务
while (nextUnitOfWork && dl.timeRemaining() > 0) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (!nextUnitOfWork && pendingCommit) {
commitAllWork(pendingCommit);
} else {
requestIdleCallbackWithExpiration(dl.timeRemaining());
}
}
function commitAllWork(fiber) {
// 遍历 Fiber 树,将 Fiber 节点转换为真实的 DOM 元素,并将其添加到页面中
fiber.effects.forEach((effect) => {
if (effect.tag === "DELETE") {
commitDeletion(effect);
} else {
const parentFiber = effect.return;
const parentDOM = getParentDOM(parentFiber);
if (effect.tag === "UPDATE") {
updateDOM(effect, parentDOM);
} else if (effect.tag === "CREATE") {
createDOM(effect, parentDOM);
}
}
});
// 更新根节点的状态
const rootFiber = getRootFiber(fiber);
rootFiber.stateNode.currentFiber = fiber;
// 清空 effects 数组
fiber.effects = [];
// 清空 pendingCommit 变量
pendingCommit = null;
}
workloop的执行时机
state变化或domRender时会触发执行workloop
react怎么中断组件的更新
使用memo对组件的props进行浅比较,如果props没有变化则不触发更新
react怎么比较更新优先级
react通过过期时间来确定更新的优先级,先过期的优先级高