React中 Reconciliation算法详解

embedded/2025/1/11 19:35:32/

React 中的 Reconciliation(协调算法)详解

Reconciliation 是 React 用来更新 UI 的核心算法。它的主要目标是在更新组件时,尽可能高效地找出需要改变的部分,并将这些变更应用到真实 DOM 中。


一、Reconciliation 的核心概念

Reconciliation 的本质是通过比较新旧虚拟 DOM 树(Virtual DOM),找出差异并更新真实 DOM。React 使用高效的 Diff 算法 来完成这一过程。

1. 为什么需要 Reconciliation?

当组件的状态或属性发生变化时,React 会重新渲染组件。但为了性能优化,React 并不会直接替换整个 DOM,而是通过 Diff 算法找到最小的变更集,减少对真实 DOM 的操作。

2. 工作原理
  • React 为每次更新生成一棵新的虚拟 DOM 树。
  • 将新旧虚拟 DOM 树进行比较。
  • 找到变化的部分并更新真实 DOM。

二、Reconciliation 的 Diff 算法

React 的 Diff 算法基于以下两个假设优化:

1. 树分层比较

React 认为 DOM 节点的跨层级移动非常少,因此仅比较同一层级的节点。

案例:跨层级变动无法识别

// 初始结构
<div><p>Hello</p>
</div>// 更新后
<span><p>Hello</p>
</span>

React 会销毁整个 <div> 和其子节点,然后重新创建 <span>,而不是移动 <p>


2. 同级节点的 key 标识

React 通过 key 属性标识列表中的节点,来优化同级节点的比较过程。

默认策略:如果没有提供 key,React 默认使用索引来标识节点。

案例

const items = ['A', 'B', 'C'];// 初始渲染
<ul><li>A</li><li>B</li><li>C</li>
</ul>// 更新:交换 B 和 C 的位置
const items = ['A', 'C', 'B'];
  • 如果没有 key,React 会将 <li>B> 替换为 <li>C>,然后重新渲染 <li>B>
  • 如果有 key(如 key="B"key="C"),React 可以识别它们仅是位置变化。

三、Reconciliation 的过程分为两步

1. 调和阶段(Reconciliation Phase)
  • 生成新的虚拟 DOM。
  • 通过 Diff 算法比较新旧虚拟 DOM,标记需要更新的部分。
  • 此阶段是可中断的,React 使用时间分片(Time Slicing)来分阶段完成。
2. 提交阶段(Commit Phase)
  • React 将调和阶段的变更应用到真实 DOM。
  • 此阶段是同步的,不可中断。

四、Diff 算法的三大策略

1. 同层比较

React 只会比较同一层级的节点,忽略跨层级的变动。

案例:节点层级变动导致重新渲染

// 初始渲染
<div><h1>Hello</h1>
</div>// 更新后
<h1><div>Hello</div>
</h1>

React 会销毁原 <div>,创建新的 <h1>,而不是试图调整层级。


2. 组件类型比较

React 会比较组件的类型:

  • 如果是相同类型组件(如同为函数组件或类组件),会复用组件实例并更新其 props
  • 如果类型不同,React 会卸载旧组件并创建新组件。

案例

// 初始渲染
function App() {return <Header />;
}// 更新后
function App() {return <Footer />;
}

React 会卸载 <Header> 并重新挂载 <Footer>


3. Key 优化列表比较

对于同级列表,key 的作用尤为重要:

  • 如果 key 相同,React 认为节点未变化,只更新位置或内容。
  • 如果 key 不同,React 认为是新的节点,会重新创建。

案例:Key 的正确使用

// 错误:使用索引作为 key
const list = items.map((item, index) => <li key={index}>{item}</li>);// 正确:使用唯一值作为 key
const list = items.map(item => <li key={item.id}>{item.name}</li>);

五、Reconciliation 中的常见问题

1. Key 的使用错误

如果列表中的 key 不唯一,可能导致性能问题或意外的 UI 错误。

案例

const list = ['A', 'B', 'C'];
// 初始渲染
<ul><li key="1">A</li><li key="1">B</li> // 错误:Key 重复<li key="2">C</li>
</ul>
2. 不必要的重新渲染

如果组件未优化,状态或属性的细微变化可能导致整个子树重新渲染。

解决方法

  • 使用 React.memo 优化函数组件。
  • 在类组件中实现 shouldComponentUpdate 或使用 PureComponent

六、性能优化建议

1. 使用唯一的 Key

在动态列表中使用唯一的 key,避免使用索引。

2. 减少不必要的 DOM 结构变更

尽量保持 DOM 的层级和结构一致,避免频繁的跨层级调整。

3. 优化子组件渲染
  • 使用 React.memo 缓存组件。
  • 使用 useCallbackuseMemo 优化回调函数和计算值。
4. 分离渲染逻辑

将复杂的 UI 分为多个独立组件,每个组件只关注自己的状态和更新。


七、案例:Reconciliation 过程演示

function App() {const [items, setItems] = React.useState(['A', 'B', 'C']);const swapItems = () => setItems(['A', 'C', 'B']);return (<div><button onClick={swapItems}>Swap</button><ul>{items.map(item => (<li key={item}>{item}</li>))}</ul></div>);
}
过程:
  1. 初次渲染时,React 会构建虚拟 DOM,并将其与真实 DOM 同步。
  2. 当点击按钮时,setItems 触发状态更新,生成新的虚拟 DOM。
  3. React 比较新旧虚拟 DOM,根据 key 找出差异,只更新位置。

八、总结

React 的 Reconciliation 是一个高效的算法,通过层级比较、组件类型比较和 key 优化,找到最小的更新路径,从而保持性能的平衡。在开发中,理解 Reconciliation 可以帮助我们编写更高效的代码,避免潜在的性能陷阱。

如果有其他问题需要进一步解释,欢迎随时提问!


http://www.ppmy.cn/embedded/153101.html

相关文章

kubernetes第七天

1.影响pod调度的因素 nodeName 节点名 resources 资源限制 hostNetwork 宿主机网络 污点 污点容忍 Pod亲和性 Pod反亲和性 节点亲和性 2.污点 通常是作用于worker节点上&#xff0c;其可以影响pod的调度 语法&#xff1a;key[value]:effect effect:[ɪˈfek…

【STM32】利用SysTick定时器定时1s

1.SysTick简单介绍 SysTick定时器是一个24位的倒计数定时器&#xff0c;当计数到0时&#xff0c;将从RELOAD寄存器中自动重装载定时初值&#xff0c;开始新一轮计数。 SysTick定时器用于在每隔一定的时间产生一个中断&#xff0c;即使在系统睡眠模式下也能工作。 关于SysTic…

《机器学习》从入门到实战——决策树

目录 一、简介 二、基本结构 三、构建过程 四、API接口解析 1、决策树--分类 &#xff08;1&#xff09;、接口调用方法 &#xff08;2&#xff09;、参数解析 2、决策树--回归 &#xff08;1&#xff09;、接口调用方法 &#xff08;2&#xff09;、参数解析 五、代…

计算机网络之---数据传输与比特流

数据传输的概念 数据传输是指将数据从一个设备传输到另一个设备的过程。传输过程涉及将高层协议中的数据&#xff08;如包、帧等&#xff09;转化为比特流&#xff0c;在物理介质上传输。 比特流的概念 比特流是数据传输中最基本的单位&#xff0c;它是由0和1组成的连续比特…

Swift UI开发指南:修饰器特性(modifiers)

SwiftUI开发指南&#xff1a;修饰器特性&#xff08;Modifiers&#xff09; 什么是修饰器&#xff1f;修饰器的特性示例&#xff1a;改变文本颜色代码&#xff1a;效果&#xff1a; 常用修饰器列表修饰器的最佳实践总结 在SwiftUI中&#xff0c;修饰器&#xff08;modifiers&am…

[免费]微信小程序(高校就业)招聘系统(Springboot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序(高校就业)招聘系统(Springboot后端Vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序(高校就业)招聘系统(Springboot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…

【大模型入门指南 07】量化技术浅析

【大模型入门指南】系列文章&#xff1a; 【大模型入门指南 01】深度学习入门【大模型入门指南 02】LLM大模型基础知识【大模型入门指南 03】提示词工程【大模型入门指南 04】Transformer结构【大模型入门指南 05】LLM技术选型【大模型入门指南 06】LLM数据预处理【大模型入门…

Spring Boot项目中增加MQTT对接

在Spring Boot项目中增加MQTT对接&#xff0c;通常涉及以下几个步骤&#xff1a; 一、搭建MQTT服务器 首先&#xff0c;你需要搭建一个MQTT服务器&#xff08;Broker&#xff09;。这可以通过多种方式实现&#xff0c;例如使用Docker来部署EMQX或Mosquitto等MQTT Broker。 以…