react fiber

112 阅读4分钟

react fiber的三个阶段

  • 调度阶段:根据任务的优先级、类型、过期时间来决定执行顺序
  • 协调阶段:React Fiber 会遍历 Fiber 树,并根据新的 props 和 state 来计算出新的 Fiber 树,并将其存储在根节点的 Fiber 对象中。
  • 提交阶段:将新的fiber树添加到真实节点中

react fiber将渲染分成多个阶段

  1. 异步渲染,在多个帧中分批处理。避免长时间阻塞
  2. 可中断,可在渲染过程中中断
  3. 优先级,根据优先级进行渲染
  4. 可预测

react fiber的执行流程

  1. 初始化阶段:React Fiber从根节点开始遍历整个组件树,创建Fiber节点,并为每个Fiber节点分配一个唯一的标识。
  1. 协调阶段: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上。

  2. 渲染阶段: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通过过期时间来确定更新的优先级,先过期的优先级高