React中 Reconciliation算法详解

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 可以帮助我们编写更高效的代码,避免潜在的性能陷阱。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950961.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 爬虫验证码识别

在我们进行爬虫的过程中&#xff0c;经常会碰到有些网站会时不时弹出来验证码识别。我们该如何解决呢&#xff1f;这里分享 2 种我尝试过的方法。 0.验证码示例 1.OpenCV pytesseract 使用 Python 中的 OpenCV 库进行图像预处理&#xff08;边缘保留滤波、灰度化、二值化、…

[离线数仓] 总结二、Hive数仓分层开发

接 [离线数仓] 总结一、数据采集 5.8 数仓开发之ODS层 ODS层的设计要点如下: (1)ODS层的表结构设计依托于从业务系统同步过来的数据结构。 (2)ODS层要保存全部历史数据,故其压缩格式应选择压缩比率,较高的,此处选择gzip。 CompressedStorage - Apache Hive - Apac…

GraphQL:强大的API查询语言

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

vue js实现时钟以及刻度效果

2025.01.08今天我学习如何用js实现时钟样式&#xff0c;效果如下&#xff1a; 一、html代码如下&#xff1a; <template><!--圆圈--><div class"notice_border"><div class"notice_position notice_name_class" v-for"item in …

Docker入门之docker基本命令

Docker入门之docker基本命令 官方网站&#xff1a;https://www.docker.com/ 1. 拉取官方镜像并创建容器&#xff08;以redis为例&#xff09; 拉取官方镜像 docker pull redis# 如果不需要添加到自定义网络使用这个命令&#xff0c;如需要&#xff0c;直接看第二步 docker r…

“深入浅出”系列之FFmpeg:(1)音视频开发基础

我的音视频开发大部分内容是跟着雷霄骅大佬学习的&#xff0c;所以笔记也是跟雷老师的博客写的。 一、音视频相关的基础知识 首先播放一个视频文件的流程如下所示&#xff1a; FFmpeg的作用就是将H.264格式的数据转换成YUV格式的数据&#xff0c;然后SDL将YUV显示到电脑屏幕上…

【JAVA基础】Collections方法的具体使用方法

java基础中Collections及collect(toList,toSet,toMap)的用法 package com.gaofeng;import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream;public class demo01 {public static void main(String[] …

深度学习知识点:RNN

文章目录 1.简单介绍2.网络结构3.应对梯度消失 1.简单介绍 循环神经网络&#xff08;RNN&#xff0c;Recurrent Neural Network&#xff09;是一类用于处理序列数据的神经网络。与传统网络相比&#xff0c;变化不是特别大&#xff0c;不如CNN的变化那么大。 为什么要有循环神经…

超完整Docker学习记录,Docker常用命令详解

前言 关于国内拉取不到docker镜像的问题&#xff0c;可以利用Github Action将需要的镜像转存到阿里云私有仓库&#xff0c;然后再通过阿里云私有仓库去拉取就可以了。 参考项目地址&#xff1a;使用Github Action将国外的Docker镜像转存到阿里云私有仓库 一、Docker简介 Do…

MySQL学习笔记(二)

一、SQL-函数 函数-介绍 函数是指一段可以直接被另一段程序调用的程序或代码。 字符串函数 示例 --concat select concat(Hello,MySql); --upper select upper(Hello); --lpad select lpad(01,5,-); --trim select trim( Hello MySQL ); --中间空格还在&#xff0c;头尾…

java mail 535 Login Fail. Please enter your authorization code to login

报错信息提示查看 https://service.mail.qq.com/detail/0/53 帮助页面意思就是说你要使用授权码登录, 但是授权码我已经正确的设置上去了 后面从 QQ邮箱出现错误 Please enter your authorization code to_邮件群发-双翼邮件群发软件官方网 看到 账户 需要是 QQ号 例如…

mysql、postgresql、druid链接池踩坑记录

The last packet successfully received from the server wIs 10,010 milliseconds ago. The last packet sent successfully to the server was 10,010 milliseconds ago.### The error may exist in URL mysql 链接字符串没有 &connectTimeout600000&socketTimeout6…

安卓NDK视觉开发——手机拍照文档边缘检测实现方法与库封装

一、项目创建 创建NDK项目有两种方式&#xff0c;一种从新创建整个项目&#xff0c;一个在创建好的项目添加NDK接口。 1.创建NDK项目 创建 一个Native C项目&#xff1a; 选择包名、API版本与算法交互的语言&#xff1a; 选择C版本&#xff1a; 创建完之后&#xff0c;可…

Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别

Spring Boot – CrudRepository 和 JpaRepository 之间的区别 Spring Boot建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。由于其快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置和设置&#xff0c;因此如今它正成为开发人员…

【网页自动化】篡改猴入门教程

安装篡改猴 打开浏览器扩展商店&#xff08;Edge、Chrome、Firefox 等&#xff09;。搜索 Tampermonkey 并安装。 如图安装后&#xff0c;浏览器右上角会显示一个带有猴子图标的按钮。 创建用户脚本 已进入篡改猴管理面板点击创建 脚本注释说明 name&#xff1a;脚本名称。…

spark汇总

目录 描述运行模式1. Windows模式代码示例 2. Local模式3. Standalone模式 RDD描述特性RDD创建代码示例&#xff08;并行化创建&#xff09;代码示例&#xff08;读取外部数据&#xff09;代码示例&#xff08;读取目录下的所有文件&#xff09; 算子DAGSparkSQLSparkStreaming…

Spring AMQP-保证发送者消息的可靠性

1. 消息发送者的可靠性 保证消息的可靠性可以通过发送者重连和发送者确认来实现 发送者重连 发送者重连机制就是在发送信息的时候如果连接不上mq不会立即结束&#xff0c;而是会在一定的时间间隔之类进行重新连接&#xff0c;连接的次数和时间都是由我们在配置文件中指定的&…

vs2022编译webrtc步骤

1、主要步骤说明 概述&#xff1a;基础环境必须有&#xff0c;比如git&#xff0c;Powershell这些&#xff0c;就不写到下面了。 1.1 安装vs2022 1、选择使用C的桌面开发 2、 Windows 10 SDK安装10.0.20348.0 3、勾选MFC及ATL这两项 4、 安装完VS2022后&#xff0c;必须安…

UnityWebGl:打包成webgl后UGUI不显示文字(中文)问题

是由于unity默认使用的是Arial,导致打包成webgl时中文不显示 解决方案&#xff1a; 可在电脑C盘下&#xff0c;路径为C:\Windows\Fonts 找个中文简体的字体文件放到unity里面&#xff0c;格式必须为. ttf

ffmpeg-avio实战:打开本地文件或者网络直播流dome

使用ffmpeg打开打开本地文件或者网络直播流的一个小dome。流程产靠ffmpeg4.x系列的解码流程-CSDN博客 #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> #include <libavutil/file.h> #include &l…