当前位置: 首页 > news >正文

React 核心原理与Fiber架构

目录

一、虚拟 DOM

二、Diffing 算法

三、Fiber 架构

四、渲染流程

1. Render 阶段(可中断异步过程)

2. Commit 阶段(同步不可中断)

五、时间切片(Time Slicing)

六、核心流程步骤总结

1. 状态更新触发

2. Render 阶段(异步可中断,构建 Fiber 树)

3. Commit 阶段(同步不可中断,更新真实 DOM)

4. 双缓存机制切换

5. 调度系统核心支撑

七、组件触发渲染的时机


相关内容: 

React Fiber 架构原理:关于 Fiber 树的一切

React核心原理浅析

二十分钟掌握React核心理念,老鸟快速入门指南


一、虚拟 DOM

React 使用虚拟 DOM 来表示 UI 的状态。虚拟 DOM 是一个轻量级的 JavaScript 对象,每个节点包含 tag(标签名)、props(属性)、children(子节点)。

核心价值在于性能优化 —— 直接操作真实 DOM 会触发浏览器重排 / 重绘,成本很高;而虚拟 DOM 先在内存中通过 Diff 算法比对状态变更,再批量更新真实 DOM,减少浏览器操作次数。

二、Diffing 算法

Diffing 算法用于比较新旧虚拟 DOM 树,以最小的操作次数将旧 DOM 树转换为新 DOM 树。

React 提出了复杂度为 O(n) 的启发式算法,通过设置 key 属性来标识一组同级子元素,从而高效地更新真实 DOM。

  1. 同层比较(Tree Diff):仅逐层对比节点,不跨层级遍历。若节点类型不同,直接销毁整棵子树及其组件实例并重建。
  2. Key 值优化:列表渲染时需为元素提供唯一 key,用于标识节点身份。通过 key,React 可识别节点的移动或复用,避免全量更新。
  3. 组件类型比对:若组件类型相同,则递归更新子节点;若类型不同,则卸载旧组件并挂载新组件。

不指定 key 的后果:

React 通过 ​​key + 组件类型​​ 的组合来识别元素的身份,没有 key 时默认使用 ​​数组索引​​ 作为 key。

若用索引作为 key,当列表项顺序变化或中间插入 / 删除元素时,React 会错误地认为大量节点需重新创建,而非移动或更新,导致不必要的 DOM 操作(如重复卸载 / 挂载组件),严重影响性能。

索引作为 key 的问题本质:

  • ​绑定问题​​:使用索引作为 key 时状态绑定到​位置​,而非​数据​​
  • DOM复用规则​​:React 只复用相同 key 对应的 DOM 节点
  • ​数据与DOM分离​​:React 更新内容但不更新状态
  • ​状态漂移​​:输入状态留在原位置,被新元素继承

使用唯一 ID 作为 key 可以解决这个问题,因为它确保状态与数据项(而非位置)保持一致关联。


三、Fiber 架构

Fiber 架构解决了传统同步渲染阻塞主线程的问题,实现可中断的异步渲染,支持时间切片和优先级调度。

Fiber 节点结构

每个组件对应一个 Fiber 节点,构成链表树(非传统递归树)。节点包含组件类型、状态、副作用标记(effectTag,如删除、新增节点)、节点指针:child(指向第一个子节点)、sibling(指向下一个兄弟节点)、return(指向父节点)。

双缓存机制

  • Current Tree:当前已渲染到页面的 Fiber 树。
  • WorkInProgress Tree:后台构建的新 Fiber 树,用于计算变更。

两棵树通过 alternate 指针关联,每次更新时新建 WorkInProgress Tree,构建完成后直接替换Current Tree,保证视图连续性。


四、渲染流程

1. Render 阶段(可中断异步过程)

构建 Fiber 链表树,通过 Diff 标记副作用(如节点增删)。

1. 深度优先遍历
从根节点开始,采用深度优先遍历,通过 beginWork 向下处理每个 Fiber 节点,逐步构建 Fiber 链表树。

2. Diffing 算法执行
对比新旧子节点,决定复用/移动/删除,并标记 effectTag(如 Placement 移动节点)。

  • 节点复用条件:父节点已复用,且 key 和 type 相同。
  • 子节点 Diff 顺序:先尝试单节点匹配,再处理多节点末尾增删(一轮循环),最后处理复杂移动场景(二轮循环),尽可能减少节点移动开销。

3. 向上回溯
当节点无子节点时,进入 completeUnitOfWork,自底向上收集 effectTag,将子节点的 effectList 合并到父节点,最终形成从根节点到叶节点的副作用链表。

4. 中断与恢复
利用时间切片(Time Slicing)将任务拆分为微任务,在浏览器空闲时执行,避免阻塞主线程。每处理完一个节点,检查剩余时间片,时间耗尽时暂停,通过全局变量保存进度,浏览器空闲时通过调度器恢复任务。

2. Commit 阶段(同步不可中断)

批量执行副作用,更新真实 DOM。此阶段必须一气呵成,确保 DOM 操作的原子性。

遍历 effectList,批量更新真实 DOM(执行 effectTag 对应的操作,如创建、删除节点)。触发回调,处理 ref 和 useEffect。


五、时间切片(Time Slicing)

时间切片策略将渲染任务拆分为微任务单元,利用浏览器空闲时段执行,提升响应性。

调度器:模拟 requestIdleCallback 功能(兼容旧浏览器),设置任务优先级,通过 MessageChannel 实现异步调度,将任务拆分为小单元,每次执行完一个单元后,检查是否有高优先级任务插队,若有则暂停当前任务。

任务优先级:分为五级(紧急交互 > 过渡动画 > 普通更新 > 延迟更新 > 过期任务),高优先级任务可插队执行,中断低优先级任务;低优先级任务可暂停或丢弃,避免占用主线程。例如,用户点击按钮时,渲染任务会被暂停,优先处理点击回调。

调度系统通过四层架构实现:

  1. SchedulerHostConfig:对接浏览器底层能力,利用 MessageChannel 计算空闲时间,提供空闲回调机制,是 Fiber 调度的基础。
  2. Scheduler:核心任务管理模块,定义五级优先级,通过双向循环链表维护任务池,实现任务的注册、取消和优先级排序,并在浏览器空闲时执行任务。
  3. SchedulerWithReactIntegration:抹平调度接口,将 Scheduler 与 React 的更新流程整合,例如在状态更新时触发调度。
  4. ReactFiberScheduler:应用层调度入口,将 React 的更新任务(如 Fiber 树的构建)包装为 Scheduler 可处理的任务,在 Render 阶段通过 shouldYield() 检查是否需要中断任务,确保主线程不阻塞。

六、核心流程步骤总结

1. 状态更新触发

  • 因用户交互(如点击)、setState 或 Hooks 更新函数调用,触发组件状态变更,生成新虚拟 DOM,启动更新流程。

2. Render 阶段(异步可中断,构建 Fiber 树)

  • 任务拆分与优先级调度:利用 时间切片 将渲染任务拆分为微任务,通过调度器按优先级异步执行,可被高优先级任务中断。
  • Fiber 树构建与 Diff 执行
    • 深度优先遍历,通过 beginWork 对比新旧虚拟 DOM,复用 key 和类型相同的节点,标记新增 / 更新 / 删除的 副作用(effectTag)
    • 子节点 Diff 按 “单节点匹配 → 末尾增删 → 复杂移动” 顺序优化,减少 DOM 操作。
  • 副作用收集:通过 completeUnitOfWork 自底向上合并副作用,形成根节点的 effectList 链表
  • 中断与恢复:每处理完一个 Fiber 节点,检查时间片是否耗尽,耗尽时暂停任务并保存进度,浏览器空闲时恢复。

3. Commit 阶段(同步不可中断,更新真实 DOM)

  • 遍历 effectList,批量执行 DOM 操作(创建、删除、更新节点),确保操作原子性。
  • 触发生命周期回调(如 useEffectref 更新),完成视图渲染。

4. 双缓存机制切换

  • 构建完成的 WorkInProgress Tree 替换为 Current Tree,通过 alternate 指针复用节点数据,保证视图连续性。

5. 调度系统核心支撑

  • 通过 四层架构(SchedulerHostConfig、Scheduler、SchedulerWithReactIntegration、ReactFiberScheduler)实现任务优先级管理、时间切片和异步调度,避免主线程阻塞。

七、组件触发渲染的时机

  • 状态(state)更新:调用更新函数导致组件状态变化。
  • props 变化:父组件传递的 props 值或引用发生改变。
  • 上下文(Context)变化:组件依赖的 Context 值更新。
  • 父组件渲染:父组件重新渲染触发子组件默认更新(未优化时)。
  • 强制更新:调用forceUpdate()跳过常规更新判断。
  • 组件 key 变化:触发旧组件卸载和新组件挂载(相当于重新渲染)。

相关文章:

  • [XILINX]ZYNQ7010_7020_软件LVDS设计
  • Spring Boot 项目初始化
  • HCIP-Datacom Core Technology V1.0_4 OSPF路由计算
  • 抽象工厂设计模式
  • 从C++编程入手设计模式——责任链模式
  • 大模型应用:如何使用Langchain+Qwen部署一套Rag检索系统
  • 【机器学习四大核心任务类型详解】分类、回归、聚类、降维都是什么?
  • OpenGL ES 中的材质
  • 分布式ID生成方式及优缺点详解
  • [特殊字符] AIGC工具深度实战:GPT与通义灵码如何彻底重构企业开发流程
  • 电脑商城--购物车
  • Camera Sensor接口协议全解析(三):移动霸主——MIPI CSI-2架构拆解
  • 【数据结构】_二叉树部分特征统计
  • rom定制系列------红米note11 5G版 MTK芯片强解bl锁修复bug 官方系统 面具root批量线刷版
  • React 新钩子useImperativeHandle
  • 华为OD机考-素数伴侣-逻辑分析(JAVA 2025B卷)
  • AWS 使用图形化界面创建 EKS 集群(零基础教程)
  • jenkins对接、jenkins-rest
  • 单例模式-Python示例
  • 如何仅用AI开发完整的小程序<4>—小程序页面创建与删除
  • 网站建设公司推/郑州网站推广方案
  • 个人做哪方面的网站/seo技术有哪些
  • 广州工程招标投标信息网/厦门网站搜索引擎优化
  • 企查查企业信息查询网站/口碑营销案例2021
  • 建筑工程管理系统平台/达州seo
  • wordpress目录安装主题/seo收费标准多少