React19 渲染流程

一、React 渲染架构

底层渲染流程大致可以分为两大阶段:render 阶段(Scheduler -> Reconciler)、commit 阶段(Renderer)

注意这里的 render 阶段和 Renderer 渲染器是两个东西!!!

主要的阶段可以总结为两个公式:

JavaScript 复制代码
const state = reconcile(update); // 通过 reconciler 计算出最新的状态
const UI = commit(state); // 通过最新的 state 渲染出 UI

二、调度器(Scheduler):渲染的入口与优先级控制

Scheduler 类似于浏览器的原生 API requestIdleCallback。React 团队没有使用 requestIdleCallback 是因为考虑为兼容性问题。

Scheduler 是整个渲染执行链条的入口,负责:

  1. 将变更转化为任务
  2. 按优先级调度任务执行
  3. 支持时间切片与可中断渲染

虽然每一帧绘制时间约为 16.66ms,但是如果屏幕没有刷新,那么浏览器会安排长度为 50ms 左右的空闲时间。

研究表明,用户操作之后,100ms 以内的响应给用户的感觉都是瞬间发生,也就是说不会感受到延迟感,因此将空闲时间设置为 50,浏览器依然还剩下 50ms 可以处理用户的操作响应,不会让用户感到延迟,这就是我们面试题中常看到的浏览器渲染每帧的空闲时间。

更新任务被排队

当你调用 setStatedispatchstartTransition、事件触发更新时,React 内部会:

Plain 复制代码
scheduleUpdateOnFiber(fiber, lane, eventTime)

这个函数是调度入口,它将更新附着在对应的 Fiber 上,并根据任务的 lane(内部优先级) 更新任务队列。

任务优先级系统(Lanes)

React 内部并不使用单一队列,而是用多 lane 机制。

优先级由 Scheduler 根据事件来源与延迟(expiration time)决定。

时间切片(Time Slicing)

Scheduler 会根据浏览器的空闲时间,决定是在当前帧继续执行还是 yield 给浏览器,这样协调器起点也是不同的:

  • performSyncWorkOnRoot(同步更新流程)
  • performConcurrentWorkOnRoot(并发更新流程)
JavaScript 复制代码
// performSyncWorkOnRoot 会执行该方法
function workLoopSync(){
  while(workInProgress !== null){
    performUnitOfWork(workInProgress)
  }
}
// performConcurrentWorkOnRoot 会执行该方法
function workLoopConcurrent() {
  // shouldYield 表明任务是否可以中断
  // 如果 workInProgress 为 null,说明已经没有下一个 FiberNode,也就是说明整颗 Fiber tree 树构建完毕
  while (workInProgress !== null && !shouldYield()) {
    // performUnitOfWork创建下一个 FiberNode,并且将已创建的 FiberNode 连接起来(child、return、sibling),形成一个链表结构的 Fiber tree
    workInProgress = performUnitOfWork(workInProgress); 
  }
}

shouldYield 判断当前帧是否该让出,避免阻塞主线程。

中断 + 恢复机制

如果有更高优先级的任务,中断当前 render,后续在合适时机 ​恢复执行​。这样能确保交互响应不会被大更新阻塞。

三、协调器(Reconciler):Fiber 树的 diff 与标记

协调器的核心任务是:

  1. 对比新旧树,确定哪些节点变化
  2. 生成 workInProgress Fiber 树
  3. 标记变化(Placement、Update、Deletion)
  4. 不直接修改 DOM,而是将变化收集起来

Fiber 数据结构

每一个组件在运行时都有一个 Fiber 节点:

  • 指向真实 DOM 实例
  • 链接 child / sibling / return 三个方向
  • 记录当前组件状态、props、更新队列、优先级等属性

这种结构不同于简单递归栈,它更像一颗​可分片执行的链表树​。

Reconciler 工作循环 ------ render 阶段

协调器是由 Scheduler 调度后执行的。React 会从 Root Fiber 开始 traverse。

a. beginWork

处理当前节点逻辑,根据类型(函数组件 / 类组件 / Fragment 等)判断需要构建哪些子 Fiber。

b. completeWork

在子节点构建完成后整合变化,把节点的更改标记写入 flags

JavaScript 复制代码
// 深度优先遍历
function performUnitOfWork(fiberNode){
  // 省略 beginWork 
  if(fiberNode.child){
    performUnitOfWork(fiberNode.child);
  }
  // 省略 CompleteWork 
  if(fiberNode.sibling){
    performUnitOfWork(fiberNode.sibling);
  }
}

整个过程构成一个 ​双缓冲机制​:

  • current Fiber(当前渲染状态)
  • workInProgress Fiber(正在构建的树)

最终 workInProgress 树会用于提交阶段。

标记变化

协调器不会直接操作 DOM,而是根据差异为每个节点打上 flags:

  • Placement:新节点插入
  • Update:属性/文本内容更新
  • Deletion:删除节点

这些标记会在提交阶段被 Renderer 读取。

四、渲染器(Renderer):真实环境应用变化

Renderer 是把协调阶段生成的变化同步应用到最终环境,比如:

  • 浏览器的 DOM
  • React Native 原生视图
  • 其他自定义渲染平台

React 通过隔离平台实现了 Renderer 的可插拔。相较于 render 阶段,此时 commit 阶段不可中断恢复。

提交阶段的三个子阶段

协调阶段完成后进入 ​commit 阶段​:

a. Before Mutation

执行 getSnapshotBeforeUpdate(类组件),记录焦点、selection(输入框光标)等。

JavaScript 复制代码
commitBeforeMutationEffects(root, finishedWork)
// 读取旧的 DOM 状态
b. Mutation

真实 DOM 操作发生位置 ​,根据 flags 做:appendChild、removeChild、update 属性、插入节点等。

然后切换 Fiber Tree(也就是上面提到的双缓冲机制,两棵树互换)。

c. Layout

useLayoutEffect 的意义是:在 DOM 更新完成后,浏览器绘制之前,同步读取或修改布局。

执行 useLayoutEffect、componentDidMount、componentDidUpdate 的回调。

注意哦!!!这里虽然 DOM 已经变更了,但是浏览器页面​还没有 paint,下一步才是绘制​。

渲染器应用 DOM 改变

JavaScript 复制代码
commitUpdate(domElement, updatePayload, type, oldProps, newProps)
// 调用一系列 DOM API 为元素属性更新赋值

这里 paint 之后,才会执行 useEffect(异步)的回调。

所以如果需要操作 DOM ,要在 useLayoutEffect 钩子执行。

相关推荐
meilindehuzi_a2 小时前
从零理解并实现 RAG:用 LangChain.js 构建语义检索问答系统
开发语言·javascript·langchain
山东云弈创峰科技3 小时前
山东云弈创峰:跨境电商AI自动化中的环境隔离与风控架构
人工智能·架构·自动化
世界哪有真情3 小时前
拿人类意识卡 AI?等于用 bug 验收正式产品
前端·人工智能·后端
鱼樱前端4 小时前
别再自己拼凑了!这款 Vue 3.5+ 严肃产品级组件库,把 AI 对话、工作流和复杂数据全包了!
javascript·vue.js·github
Listen·Rain4 小时前
Vue3:setup详解
前端·javascript·vue.js
Patrick_Wilson5 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen5 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh6 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn6 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒7 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端