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 流畅度
相关推荐
candyTong21 小时前
一觉醒来,大模型就帮我排查完页面性能问题
前端·javascript·架构
魔术师Grace21 小时前
我给 AI 做了场入职培训
前端·程序员
玩嵌入式的菜鸡21 小时前
网页访问单片机设备---基于mqtt
前端·javascript·css
前端一小卒1 天前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
滑雪的企鹅.1 天前
HTML头部元信息避坑指南大纲
前端·html
一拳不是超人1 天前
老婆天天吵吵要买塔罗牌,我直接用 AI 2 小时写了个在线塔罗牌
前端·ai编程
excel1 天前
如何解决 Nuxt DevTools 中关于 unstorage 包的报错
前端
Rust研习社1 天前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust
C澒1 天前
AI 生码 - API2Code:接口智能匹配与 API 自动化生码全链路设计
前端·低代码·ai编程
浔川python社1 天前
HTML头部元信息避坑指南技术文章大纲
前端·html