react fiber与事件循环

react fiber与事件循环

在学习react fiber与事件循环的过程中,我一直有这么一个困扰,那就是单独学习的时候,我好像都理解,但是我的脑袋里面没有一条清晰的时间线,到底react的任务从浏览器的视角来看到底是怎么执行,怎么渲染的,为此,我专门将react fiber与浏览器的事件循环放在一起进行学习,希望能够得到更清晰的学习结论。阅读本文,默认对事件循环和react fiber架构有一定了解。

事件循环

  • 什么是浏览器的事件循环机制?

浏览器的事件循环是一套"协调机制",用来在单线程的 JavaScript 环境中,合理安排同步代码、异步任务和页面渲染的执行顺序。

  • 为什么要有事件循环机制

js是单线程的(根本原因),但是它需要处理很多事情。

  1. 主线程的代码
  2. 处理dom
  3. 页面渲染
  4. 相应用户事件

很显然这些事情是需要一个"排队"的机制。

还有一些异步操作的存在:

  1. setTimeout
  2. 网络请求
  3. DOM 事件
  4. Promise

这些任务并不是js引擎去执行,而是web API,执行完后通知js的主线程,js再去处理这些回调。

结论:我们需要一套机制去处理上面所说的排队,去处理这些异步操作回调执行的时机。这就是事件循环。

那关于具体的事件循环的学习本文不涉及,这里只给出结论

一次事件循环 = 执行一个宏任务 → 清空所有微任务 → 进行页面渲染(可能)

  1. 先执行 一个宏任务
  2. 然后 执行所有微任务
  3. 然后浏览器 可能进行一次渲染
  4. 再进入下一轮循环

React fiber策略

  • 什么是react fiber

简单来说,其实是 React 为了实现 可中断、可调度、可增量的渲染 所设计的内部机制。

我们来看一个简单的例子:

css 复制代码
const elementTree = {
    type: "div",
    props:{
        children: [
            {
                type: "span",
                key: "span-a"
                props: {
                    children: [
                        {
                            type: "TEXT_ELEMENT",
                            props: {
                              nodeValue: "aaaaa",
                              children: [],
                            },
                        }
                    ]
                }
            },
            {
                type: "span",
                key: "span-b"
                props: {
                    children: [
                        {
                            type: "TEXT_ELEMENT",
                            props: {
                              nodeValue: "bbbbb",
                              children: [],
                            },
                        }
                    ]
                }
            }
        ]
    }
}

这是对应的fiber的链表图

fiber采用深度优先遍历,对每一个fiber节点都采用浏览器空闲时间进行计算,直到整棵树都计算完成,最后才提交渲染。

问题?

  1. 所谓的可中断,空闲时间,到底是指的浏览器事件循环的什么阶段
  2. fiber的计算,是在什么时候

一条完整时间线:React Fiber × 浏览器

场景:用户点击按钮 → React 更新 UI


🟦 阶段 1:浏览器的事

点击事件发生 `` ↓ `` 事件系统 `` ↓ ``宏任务(onClick)


🟩 阶段 2:React 开始工作(JS 阶段)

scss 复制代码
onClick() {
  setState(...)
}

React 做了什么:

  1. 不改 DOM
  2. 创建 Update
  3. 把 Update 挂到 Fiber
  4. 标记优先级(Lane)
  5. 请求调度(scheduleUpdateOnFiber)

👉 React 此时仍然完全在 JS 世界


🟨 阶段 3:Fiber Render 阶段(可中断)

React 在做:

  • 构建新的 Fiber Tree
  • Diff(Reconciliation)
  • 计算 effect list

⚠️ 这个阶段:

  • 不触碰 DOM
  • 可以被中断
  • 可能跨多帧

🟥 阶段 4:Commit 阶段(不可中断)

React 选择一个安全时机(通常靠近帧边界):

  1. 执行 DOM 操作
  2. 执行 layout effects
  3. 更新 refs

👉 这一刻, React 才真正影响浏览器渲染


🟪 阶段 5:浏览器渲染

rAF(浏览器) ``↓ Style Layout Paint Composite


React 和浏览器的分工一句话总结

React 决定"改什么"和"什么时候可以改", 浏览器决定"什么时候画"和"怎么画"。

React fiber实现的核心api

  1. MessageChannel:推进计算,立即排队下一个宏任务
  2. requestAnimationFrame:Commit 阶段对齐浏览器帧

Fiber 调度的最后一部分

MessageChannel 如何推进各个工作单元

在 React Fiber 中,整个组件树的更新被拆分成一个个 Fiber 单元(unit of work)。每个单元负责:

  • 构建 Fiber 节点
  • 进行 Diff 对比
  • 计算 effect list

为什么 MessageChannel 被使用?

  • Fiber 的 Render 阶段是可中断的,需要在主线程空闲时继续推进任务
  • MessageChannel 可以立即排队一个宏任务,在当前宏任务执行完成后尽快执行下一轮 Fiber 计算。
  • 这意味着,React 不会一次性阻塞主线程去渲染整个组件树,而是切片执行,每次执行一小部分 Fiber 单元

MessageChannel 任务所在队列:

  • MessageChannel 的 onmessage 回调属于宏任务队列
  • 宏任务执行完成后,微任务队列会被清空,然后浏览器判断是否渲染。
  • 因此 Fiber 的 Render 阶段是宏任务中逐片推进的

requestAnimationFrame 的使用时机

  • 作用:与浏览器渲染帧同步,保证 Commit 阶段操作 DOM 时不掉帧。

  • 使用时机

    • 当 Fiber Render 阶段完成或达到时间片限制时,Scheduler 会判断是否需要提交 DOM。
    • Commit 阶段执行前,如果希望和浏览器下一帧对齐 ,就会通过 requestAnimationFrame 调用 Commit 阶段回调。
  • 为什么要使用 rAF

    • 避免直接提交 DOM 时阻塞渲染
    • 保证渲染和浏览器帧同步,提高 UI 流畅度
    • 避免计算阶段占用过长时间造成掉帧

结合事件循环的完整执行流程

以一次用户点击触发更新为例:

从上图我们可以简单的看出,react fiber的计算过程跨越了多次的事件循环,是否可中断的判断放在每一次的工作单元进行判断。且工作单元(nextUnitOfWork)的计算是放在宏任务中

总结:

  1. MessageChannel 推进 Fiber 工作单元

    1. 在宏任务队列中运行
    2. 逐片执行 Render 阶段
    3. 可以中断,分片执行以保证主线程空闲
  2. requestAnimationFrame 使用

    1. 对齐 Commit 阶段和浏览器渲染帧
    2. 避免掉帧,提高 UI 流畅度
相关推荐
Mr_chiu1 天前
告别“代码屎山”:用Cursor系统重构遗留前端项目
前端·cursor
前端不太难1 天前
用一张“状态扩散图”,定位 RN 列表性能风险
react.js·harmonyos
LC同学479811 天前
工程化实战:uniapp基于路由的自动主题切换体系
前端
莫比乌斯环1 天前
【安全专项】如何成为一名“火眼金睛”的安卓侦探?
前端·代码规范
LC同学479811 天前
深入解析:uniapp单仓库多应用(SaaS 化)架构
前端
程序员鱼皮1 天前
从夯到拉,锐评 39 个前端技术!
前端·程序员·编程语言
凌览1 天前
0成本、0代码、全球CDN:Vercel + Notion快速搭建个人博客
前端·后端
fe小陈1 天前
react-nil 逻辑渲染器
react.js
该换个名儿了1 天前
Vue3中,我的Watch为什么总监听不到数据?
前端·javascript·vue.js