第十四章 DOM的Diff算法与key
React使用Diff算法来比较虚拟DOM树和真实DOM树之间的差异,并仅更新必要的部分,以提高性能。key的作用是在Diff算法中帮助React确定哪些节点已更改,哪些节点已添加或删除。
我们以案例来说明。
使用索引值和唯一ID作为key的效果
- 1、使用索引值作为key
class Person extends React.Component {state = {persons: [{id:1,name:'张三',age: 20},{id:2,name:'李四',age: 21},]}// 增加人员addPerson = () => {let {persons} = this.stateconst user = {id:persons.length + 1,name:'王麻子',age:22}persons = [user,...persons]this.setState({persons})}render(){const {persons} = this.statereturn (<div><h1>验证diff算法</h1><button onClick={this.addPerson}>增加人员</button>{persons.map((val,idx)=> {const info = "姓名:"+val.name+ "===年龄:" + val.ageconsole.log(idx,info)return (<div key={idx}>{info}</div>)})}</div>)}}// 2、将虚拟DOM渲染到页面,标签必须闭合ReactDOM.render(<Person/>, document.getElementById('app'))
以上代码我们使用的是索引值idx来作为标签的key值,渲染到页面,当我点击【增加人员】的按钮时,会在persons的状态值里面最前方插入一个新的人员信息,然后react在render到页面中去。其页面效果如下:

- 2、使用id作为key值
class Person extends React.Component {state = {persons: [{id:1,name:'张三',age: 20},{id:2,name:'李四',age: 21},]}// 增加人员addPerson = () => {let {persons} = this.stateconst user = {id:persons.length + 1,name:'王麻子',age:22}persons = [user,...persons]this.setState({persons})}render(){const {persons} = this.statereturn (<div><h1>验证diff算法</h1><button onClick={this.addPerson}>增加人员</button>{persons.map((val,idx)=> {const info = "姓名:"+val.name+ "===年龄:" + val.ageconsole.log(idx,info)return (<div key={val.id}>{info}</div>)})}</div>)}}// 2、将虚拟DOM渲染到页面,标签必须闭合ReactDOM.render(<Person/>, document.getElementById('app'))
以上代码我们使用id来作为标签的key值,但是这里的效果和我们看到的是一样的,但是在react中处理的方式是不一样的,后续我们继续讨论。

使用索引值和唯一ID作为key的区别
我们对以上案例做一下修改,我们增加一个输入框,在看看其效果。
- 1、使用索引值作为key
class Person extends React.Component {state = {persons: [{id:1,name:'张三',age: 20},{id:2,name:'李四',age: 21},]}// 增加人员addPerson = () => {let {persons} = this.stateconst user = {id:persons.length + 1,name:'王麻子',age:22}persons = [user,...persons]this.setState({persons})}render(){const {persons} = this.statereturn (<div><h1>验证diff算法</h1><button onClick={this.addPerson}>增加人员</button>{persons.map((val,idx)=> {const info = "姓名:"+val.name+ "===年龄:" + val.ageconsole.log(idx,info)return (<div key={idx}>{info} <input defaultValue={info} type="text"/></div>)})}</div>)}}// 2、将虚拟DOM渲染到页面,标签必须闭合ReactDOM.render(<Person/>, document.getElementById('app'))
直接看效果:

姓名:王麻子===年龄:22 ----输入框:姓名:张三===年龄:20
姓名:张三===年龄:20 ----输入框:姓名:李四===年龄:21
姓名:李四===年龄:21 ----输入框:姓名:李四===年龄:21
在以上效果图上我们发现了严重的错误:输入框的内容与其人员信息不一致,这是为什么呢?
- 2、使用ID作为key
class Person extends React.Component {state = {persons: [{id:1,name:'张三',age: 20},{id:2,name:'李四',age: 21},]}// 增加人员addPerson = () => {let {persons} = this.stateconst user = {id:persons.length + 1,name:'王麻子',age:22}persons = [user,...persons]this.setState({persons})}render(){const {persons} = this.statereturn (<div><h1>验证diff算法</h1><button onClick={this.addPerson}>增加人员</button>{persons.map((val,idx)=> {const info = "姓名:"+val.name+ "===年龄:" + val.ageconsole.log(idx,info)return (<div key={val.id}>{info} <input defaultValue={info} type="text"/></div>)})}</div>)}}// 2、将虚拟DOM渲染到页面,标签必须闭合ReactDOM.render(<Person/>, document.getElementById('app'))
直接看效果:

姓名:王麻子===年龄:22 ----输入框:姓名:王麻子===年龄:22
姓名:张三===年龄:20 ----输入框:姓名:张三===年龄:20
姓名:李四===年龄:21 ----输入框:姓名:李四===年龄:21
在以上效果图上我们发现了人员信息与输入框的信息一致,并没有发生什么错误,这是为什么呢?
分析索引值与ID值作为key的原理
- 分析key值是索引值时的流程

根据上图我们可以知道使用索引值作为key的时候,三条数据基本上都要生成新的DOM,而输入框的值因为与旧的虚拟DOM比较内容一致,导致与新的数据不一致的结果。
- 分析key值是ID值的流程

根据上图我们可以知道使用唯一ID作为key值时,比较第一条数据时key值就不存在需要生成新的虚拟DOM,而后面两条的key值与旧的虚拟DOM一致,可以复用旧的真实DOM且不需要生成新的DOM,减少成本,这样使得性能更好。
小总结
结果案例演示我们知道为什么遍历列表时,key最好不要用index,而是使用唯一标识,以此来减少成本,提高性能。
- 虚拟DOM中key的作用
1、简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
2、详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM,随后渲染到到页面
- 用index作为key可能会引发的问题
1、若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2、如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
3、注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
- 开发中如何选择key?
1、最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2、如果确定只是简单的展示数据,用index也是可以的。
相关文章:
第十四章 DOM的Diff算法与key
React使用Diff算法来比较虚拟DOM树和真实DOM树之间的差异,并仅更新必要的部分,以提高性能。key的作用是在Diff算法中帮助React确定哪些节点已更改,哪些节点已添加或删除。 我们以案例来说明。 使用索引值和唯一ID作为key的效果 1、使用索引…...
MySQL调优
MySQL调优常见的回答如何回答效果更好业务层的优化如果只能用mysql该如何优化代码层的优化SQL层面优化总结常见的回答 SQL层面的优化——创建索引,创建联合索引,减少回表。再有就是少使用函数查询。 回表指的是数据库根据索引(非主键&#…...
《Flutter进阶》flutter升级空安全遇到的一些问题及解决思路
空安全出来挺久了,由于业务需求较紧,一直没时间去升级空安全,最近花了几天去升级,发现其实升级也挺简单的,不要恐惧,没有想象中的多BUG。 flutter版本从1.22.4升到3.0.5; compileSdkVersion从1…...
最值得入手的五款骨传导耳机,几款高畅销的骨传导耳机
骨传导耳机是一种声音传导方式,主要通过颅骨、骨骼把声波传递到内耳,属于非入耳式的佩戴方式。相比传统入耳式耳机,骨传导耳机不会堵塞耳道,使用时可以开放双耳,不影响与他人的正常交流。骨传导耳机不会对耳朵产生任何…...
HashMap源码分析 (1.基础入门) 学习笔记
本章为 《HashMap全B站最细致源码分析课程》 拉钩教育HashMap 学习笔记 文章目录1. HashMap的数据结构1. 数组2. 链表3. 哈希表3.1 Hash1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。 1. 数组 在生成数组的时候数…...
6 使用强制类型转换的注意事项
概述 在C语言中,强制类型转换是通过直接转换为特定类型的方式来实现的,类似于下面的代码。 float fNumber = 66.66f; // C语言的强制类型转换 int nData = (int)fNumber; 这种方式可以在任意两个类型间进行转换,太过随意和武断,很容易带来一些难以发现的隐患和问题。C++为…...
Leetcode.939 最小面积矩形
题目链接 Leetcode.939 最小面积矩形 Rating : 1752 题目描述 给定在 xy平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴。 如果没有任何矩形,就返回 0。 示例 1: 输入࿱…...
Springboot项目快速实现过滤器功能
前言很多时候,当你以为掌握了事实真相的时间,如果你能再深入一点,你可能会发现另外一些真相。比如面向切面编程的最佳编程实践是AOP,AOP的主要作用就是可以定义切入点,并在切入点纵向织入一些额外的统一操作࿰…...
基于springboot的简历系统的实现
摘 要 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,简历系统当然也不能排除在外。简历系统是以实际运用为开发背景,运用软件工程原理和开发方法,采用…...
Vue3中watch的用法
watch函数用于侦听某个值的变化,当该值发生改变后,触发对应的处理逻辑。 一、watch的基本实例 <template><div><div>{{ count }}</div><button click"changCount">更改count的值</button></div> …...
MS python学习(18)
学习Pandas.DataFrame(2) load csv(comma seperated variable) files to DataFrame and vice versa upload csv files read/write csv files load data into jupyter notebook, create a new folder and then upload the csv files into it. (CSV comma seperated variable)…...
java笔记
前言 以下是一名java初学者在自学过程中所整理的笔记,欢迎大家浏览并留言,若有错误的地方请大家指正。 java语言特性 简单性:相对于其他编程语言而言,java较为简单,例如:java不再支持多继承,C…...
对象的构造及初始化
目录 一、如何初始化对象 二、构造方法 1.概念 2.特性 三、默认初始化 四、就地初始化 总结 一、如何初始化对象 在Java方法内部定义一个局部变量的时候,我们知道必须要进行初始化。 public class Test4 {public static void main(String[] args) {//未初始化…...
Socket 读取数据
1. Socket 配置参数中添加 1.1 读取 Socket 字节时的字节序 1.2 读取数据时,单次读取最大缓存值 1.3 从 Socket 读取数据时,遵从的数据包结构协议 1.4 服务器返回数据的最大值,防止客户端内存溢出 /*** Description: Socket 配置参数*/public…...
小白的Git入门教程(一)
这是本人的git的入门过程仅供参考 先是在官网下载git版本下载链接,安装步骤可以搜索其他大神的文章然后就是创建一个属于你的git本地库首先是创建一个文件夹作为根目录,这里我创建了一个叫test_git文件夹紧接着便进去新建文件夹,点击这里的g…...
第一个Vue程序
第一个Vue程序 <body> <!--view层 变成了一个模板--> <div id"app">{{message}} </div><!--导入vue.js--> <script src"https://cdn.jsdelivr.net/npm/vue2.5.16/dist/vue.min.js"></script> <script>va…...
2023上学期学习计划
目前:根据答辩的情况来看,目前去项目组,着重写好算法是相对较优的打算,先将项目写好,之后着重提升算法水平,这学期主要啃《算法导论》与《大话数据结构》这俩本书,同时刷题量要达到160题 四月份…...
深入了解MySQL锁机制及应用场景
深入了解MySQL锁机制及应用场景锁的概述锁的分类锁的应用场景数据库事务管理多线程程序开发数据库的备份和恢复对于在线游戏等高并发应用场景锁的具体使用方法锁的应用实例总结锁的概述 MySQL锁是操作MySQL数据库时常用的一种机制。MySQL锁可以保证多个用户在同时执行读写操作…...
Java类和对象
目录 一、什么是面向对象? 二、类与对象的基本概念 1.类 2.对象 三、类的定义格式 四、类与对象的定义与使用 1.什么是实例化 2.实例化对象 3.类的使用 4.类与对象的说明 总结 一、什么是面向对象? 面向对象是一种现在最为流行的程序设计方法&a…...
aspnet053+sqlserver在线考试系统xns
目 录 基于.NET的考试系统 1 摘 要 3 前 言 4 第一章 系统概述 5 1.1 本课题的研究意义 5 1.2 本论文的目的及内容 5 第二章 在线考试系统概述 7 2.1 现行在线考试系统现状 7 2.2 电子管理平台的开发方法介绍 8 2.2.1 B/S体系结构 8 2…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
