Vue - v-for 中为什么不能用 index 作为 key

2023-05-16

目录

1、先来看一个例子

2、上述问题的解决方法

3、高效的 Diff 算法原理

4、不能用 index 作为 key


这是一篇脱坑日记,在做项目的过程中,我使用了 v-for 渲染子组件时,并将 index 绑定给了 key,这一行为导致删除操作会误删子组件,实际上删除的组件并不是你预期的那个。而且我在排查错误的过程中打印 log 的数据信息均正常,唯独在执行删除操作时出现异常。

深陷其坑而不知具体原因,询问大佬后恍然大悟,究其根源还是自己太年轻,只知道使用 v-for 的时候需要绑定 key,却不知道在复杂情况下不能用 index 作为 key,特此总结这篇博客。

1、先来看一个例子

假设你有三个子组件,每个子组件里面有一个「有状态的」孙子组件。现在用户点击删除按钮,把第二个子组件删掉了,请问结果是怎样的?你可能会说,那还用问?当然是 2 消失了,因为 data 里的数组从 [1,2,3] 变成了 [1,3]。实际上你没有考虑全面:注意看图中的绿色正方形没有被删除。

原因很简单,你认为你删除了 2,但 Vue 会认为你做了两件事:(1)把 2 变成了 3、(2)然后把 3 删除。

Vue 为什么要舍近求远呢?看看这两个数组:[1,2,3] 和 [1,3]。人类会说,这不就是少了个 2 吗?但是计算机会怎么对比数组?遍历!首先对比 1 和 1,发现 [ 1 没变 ];然后对比 2 和 3,发现 [ 2 变成了 3 ];最后对比 undefined 和 3,发现 [ 3 被删除了」。所以计算机的结论是:[ 2 变成了 3 ] 以及 [ 3 被删除了 ]。

  • 既然 [1 没变】,那么就地复用之前的 1 和三角形就好了。
  • 既然 [ 2 变成了 3 ],那么正方形左边的 2,当然要改成 3。里面的正方形就地复用(正方形没有被删除)。因为正方形是孙子元素的 data,不受 [ 2 变成 3 ] 的影响,所以可以就地复用。
  • 既然 [ 3 被删除了」,之前的 [圆形] 当然应该被删掉,里面的子元素也要删除。

2、上述问题的解决方法

怎么解决这个问题呢?怎么让 Vue 知道我删除的是第二个,不是第三个?用 id 作为 key 就行了,不信你再看:

我们以计算机的角度来思考一下:原本的数组是 [{id:1,value:1},{id:2,value:2].{id:3,value:3],点击删除之后的数组是 [{id:1,value:1},{id:3,value:3}],Vue 会在删除操作后执行 DOM diff 算法,对比前后两次 dom 节点的异同:

  • 首先发现 id 从 1 2 3 变成了 1 3,说明第二项被删除了
  • 然后依次对比 id:1 的项和 id:3 的项,发现删除前后这两个节点没变化。

所以计算机得出结论:第二项被删除了。符合人类预期!代码示例

3、高效的 Diff 算法原理

其实不只是 Vue,React 中在执行列表渲染时也会要求给每个组件添加上 key 这个属性。要解释 key 的作用,不得不先介绍一下虚拟 DOM 的 Diff 算法了。Vue 和 React 都实现了一套虚拟 DOM,使我们可以不直接操作 DOM 元素,只操作数据便可以重新渲染页面。而隐藏在背后的原理便是其高效的 Diff 算法。

Vue 和 React 的虚拟 DOM 的 Diff 算法大致相同,其核心是基于两个简单的假设:

  • 1. 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。
  • 2. 同一层级的一组节点,他们可以通过唯一的id进行区分

基于以上这两点假设,使得虚拟 DOM 的 Diff 算法的复杂度从 O(n3) 降到了 O(n)。看图分析:

当页面的数据发生变化时,Diff 算法只会比较同一层级的节点:

  • 如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
  • 如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。

当某一层有很多相同的节点时,也就是列表节点时,Diff 算法的更新过程默认情况下也是遵循以上原则。比如下面这种情况,我们希望可以在 B 和 C 之间加一个 F,Diff 算法默认执行起来是这样的:

即把 C 更新成 F,D 更新成 C,E 更新成 D,最后再插入 E,是不是很没有效率?

所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

所以一句话,key 的作用主要是为了高效的更新虚拟 DOM。另外 Vue 中在使用 相同标签名元素的过渡切换 时,也会使用到 key 属性,其目的也是为了让 Vue 可以区分它们,否则 vue 只会替换其内部属性而不会触发过渡效果。

4、不能用 index 作为 key

为什么不能用 index 作为 key,如果你用 index 作为 key,那么在删除第二项的时候,index 就会从 1 2 3 变成 1 2(而不是 1 3),那么 Vue 依然会认为你删除的是第三项。也就是会遇到上面一样的 bug。

所以,永远不要用 index 作为 key。永远不要!除非你是大神。能清楚地知道如何解决 index做 key 带来的 bug。有人说简单的场景可以用 key。问题在于,你如何确保需求会一直保持简单?只要出现了删除一项或新增一项的需求,而且这一项里面含有子组件,上面说的 bug 就有可能出现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue - v-for 中为什么不能用 index 作为 key 的相关文章

  • 模拟人类在网站上点击“ENTER”键[重复]

    这个问题在这里已经有答案了 在 Javascript JQuery 中可能吗 我的网站上有一个按钮 当有人单击该按钮时 我需要模拟人类按下 Enter 键 例子 我点击按钮 然后它自动按下 Enter 键 我从以下代码中修改了这段代码这个答
  • 迭代 hastable 键的枚举会引发 NoSuchElementException 错误

    我正在尝试使用枚举来迭代哈希表中的键列表 但是我在列表中的最后一个键处不断收到 NoSuchElementException Hashtable
  • 获取 AWS S3 存储桶中对象的所有版本?

    我已在存储桶上启用对象版本控制 我想获取该存储桶内密钥的所有版本 但我找不到这样做的方法 如何使用 S3 API 来实现这一目标 所以 今天早上我遇到了这堵砖墙 事实证明 这件看似微不足道的事情做起来非常困难 您想要的 API 是获取桶对象
  • 在复杂的 React 组件中查找重复的键

    我有一个反应组件 它一次生成许多键 我不确定哪个不是唯一的 错误如下 有什么简单的方法可以帮助调试吗 谢谢 React js 19500 警告 数组或迭代器中的每个子项都应该有一个唯一的 key 属性 检查渲染方法MyGrid 请参阅 ht
  • 在 TreeMap、HashMap 或 LinkedHashMap 中存储具有重复键的值

    我目前正在开展一个项目 在该项目中我从社会保障网站检索有关姓名的数据 基本上我得到了一个数字x 以及年份y和z 我必须返回 y 到 z 每年的前 x 个名字 所以从网站返回的数据是姓名 排名和年份 我必须将返回的每个名称输入到 TreeMa
  • 将字符转换为键

    我有一个特殊的字符 我想将其转换为键 我目前正在使用这个 Keys k Keys 在调试时 我发现 k 等于 L按钮 R按钮 M按钮 返回 空间类型 System Windows Forms Keys k 的键码应该是 111 注意 该代码
  • 在 R 中向多直方图添加关键图例

    如何在下面的图中添加关键图例 我希望在右上角的某个地方有一个关键图例 其中有两个短水平颜色条 红色的应该说 整形手术出了问题 蓝色的应该说 德国 我使用以下代码来生成该图 bar2 lt read table div ana mut bar
  • WPF 中 DataTemplate 中的 x:Key、x:Name 和 x:UID 有什么区别?

    我正在尝试在 WPF 中创建动态选项卡 并且正在尝试编写一个仅适用于某些选项卡项目的内容模板 我希望能够为内容模板创建一个标识符 以便我可以在后面的代码中引用它 这样我就可以有选择地将它应用于单个 TabControl 中的某些选项卡 但是
  • 有没有办法让字典键成为范围?

    如果这很明显 请原谅我 但我对 Python 非常非常陌生 我已经找到了从字典中获取多个键的方法 但这不是我想要做的 基本上我正在寻找这样的东西 my dict 1 10 foo 11 20 bar 91 100 baz 但其中的键实际上不
  • 如何选择 jq 中作为变量提供的键的值?

    如果这是输入 a key 2 another key 100 one more key 4 2 通过提供键的名称作为变量来选择这些键的值的最佳方法是什么 理想情况下 我正在寻找类似的东西 a key as key key 但这会导致语法错误
  • 如何按值(DESC)然后按键(ASC)对字典进行排序?

    就在发现了惊人的事情之后sorted 我又陷入困境了 问题是我有一本以下形式的字典string key integer value 我需要按整数值的降序对它进行排序 but如果两个元素具有相同的值 则按键的升序排列 一个更清楚的例子 d b
  • 如何从python字典中的给定名称获取键

    我有一个变量叫做 anime dict which contains a dictionary of lists of objects as shown below JI2212 Inu Yasha year 1992 rating 3 E
  • Outlook 添加、文本框、删除\退格键不起作用

    我开发了一个 Outlook 插件 自定义任务窗格 在用户控件中带有 Web 浏览器 当我在网络浏览器的文本框中写入内容时 退格键或删除按钮旁边的所有功能都运行良好 但我无法使用这些键 我是否遗漏了什么 我迟到了几年 但我设法解决了这个问题
  • openssl-使用密钥和 IV 解密 Base64 字符串

    我正在尝试解密已在 openssl 中使用 aes256 加密的 base64 字符串 我获得了会话密钥和 IV 它们是用我的密钥加密的 我将它们转换为十六进制 以便可以使用以下 openssl 命令 openssl enc d aes25
  • 如何在C#中捕获键盘上的按键

    我有个问题 我需要写一个C 程序 输入 允许用户输入多行文本 按 Ctrl Enter 完成输入 输出 标准化 按照时间增加的正确顺序重新排列行 我尝试过 但我不知道如何从键盘捕获 Ctrl Enter 我期望输出像 例子 Created
  • 如何向 Rails 中的表追溯添加主键?

    我创建了一个没有主键的表 id gt false 但现在它又回来了 我的应用程序已经投入生产 我不能只是放弃它并重新创建另一个应用程序 有没有办法运行迁移以将另一个自动增量主键列添加到我的表中 在迁移中添加主键的命令是 add column
  • Google App Engine 密钥中允许使用哪些字符?

    在测试我的 Google App Engine 应用程序时 我搜索包含应用程序引擎密钥的链接 例如 story ag5yZXBsaWUtdGVzdGluZ3IMCxIFU3RvcnkY w0M 这些键中允许使用哪些字符 我一直在使用正则表达
  • Oh-my-zsh 哈希(井号)符号错误模式或未找到匹配项

    我很确定是与我的 Oh my zsh 配置相关的东西 但我不知道它是什么 当我在 git 命令中使用 符号时 但也适用于其他所有命令 例如 ls 2 我收到 错误模式 错误或 找不到匹配项 我猜是要计算一些东西 但我找不到在哪里配置它 I
  • 在 django 模板中显示字典键

    我想知道如何在 django 模板中显示字典键本身 字典示例 resources coin coin grain grain iron iron stone stone wood wood 模板 b Coin b upgrade coin
  • 简单、安全的API认证系统

    我有一个简单的 REST JSON API 供其他网站 应用程序访问我网站的一些数据库 通过 PHP 网关 基本上该服务的工作原理如下 调用 example com fruit orange 服务器返回有关橙子的 JSON 信息 问题是 我

随机推荐

  • 11- OpenCV进行目标追踪 (OpenCV系列) (机器视觉)

    知识要点 1 OpenCV目标跟踪算法 的使用大概可以分为以下几个步骤 创建MultiTracker对象 trackers 61 cv2 legacy MultiTracker create 读取视频 或摄像头数据 cap 61 cv2 V
  • Django密码的哈希算法储存

    我们知道密码是用户的隐私数据 xff0c 我们不能将真实的密码值储存在数据库中 xff0c 这样是及其不安全的 xff0c 因此我们可以用哈希算法来将一串明文密码转化为一串不可逆的值 xff0c 也就是说即使有人拿到了数据库中的密码 xff
  • 四轴飞行器——电调校准

    电调是驱动电机用的调速器 电调的作用 xff1a 电机的电流很大 xff0c 通常每个电机正常工作时的平均电流在3A左右 xff0c 如果没有电调的存在 xff0c 飞控板的I O口无法承受这样大的电流 电子调速器负责使电机运行在飞控 xf
  • 异常检测之集成方法

    感谢datawhale大部队 xff01 一 前言 背景 xff1a 在现实异常检测业务场景中 xff0c 数据集是多维度 xff08 通常是成百上千 xff09 的 xff0c 随着维度的增加 xff0c 数据空间的大小 xff08 体积
  • Spring AOP代码实现:实例演示与注解全解

    1 理解AOP 1 1 什么是AOP AOP xff08 Aspect Oriented Programming xff09 xff0c 面向切面思想 xff0c 是Spring的三大核心思想之一 xff08 两外两个 xff1a IOC
  • Windows下首次安装TensorFlow失败

    TensorFlow是一个基于数据流编程 xff08 dataflow programming xff09 的符号数学系统 xff0c 被广泛应用于各类机器学习 xff08 machine learning xff09 算法的编程实现 xf
  • Mybatis之使用注解开发CRUD

    上一篇演示了如何使用XML来操作Mybatis实现CRUD xff0c 但是大量的XML配置文件的编写是非常烦人的 因此 Mybatis也提供了基于注解的配置方式 xff0c 下面我们来演示一下使用接口加注解来实现CRUD的的例子 首先是创
  • 第16届智能车竞赛双车接力组—直立车经验语录

    第16届智能车竞赛双车接力组 直立车经验语录 前言直立环核心控制算法 串级PID转向环控制算法算法框架搭车方法波形拟合调车方法角速度环整定方法角度环整定方法速度环整定方法转向环整定方法其他问题 END 前言 这是我第一次参加智能车竞赛 xf
  • 时序异常检测方法总结

    异常检测 xff08 Anomaly detection xff09 是时序数据分析最成熟的应用之一 xff0c 目的是从正常的时间序列中识别不正常的事件或行为的过程 异常类型 xff1a 点异常 xff0c 上下文异常 xff0c 集合异
  • 中科大 2019 大数据学院 计算机专业 复试经验分享(一)

    复试已经过去很长时间了 xff0c 从拟录取之后就在马不停蹄的找导师 xff0c 确认 xff0c 沟通暑假学习内容 xff0c 旅行 xff0c 报道等等 现在已经有时间可以好好总结一下复试踩过的坑 大数据学院复试场景复刻 xff1a 复
  • Robocup 仿真2D 学习笔记(二) 球队代码编译和上场

    环境的一些问题 最近在使用rcssserver 和 rcssmonitor时可能会遇到 configure失败的问题 xff0c 可以检查一下文件夹是否有makefile文件 xff0c 如果是编译后的包直接sudo make instal
  • Ubuntu16.04+RTX3090+python3+cuda11.1+ CUDNN  8.04+anaconda3+pytorch-nightly深度学习环境搭建实录

    硬件信息 cpu Intel R Core TM i7 10700 CPU 64 2 90GHz 显卡 GeForce RTX 3090 网卡 Ethernet Connection 17 I219 V 内存 62GiB System me
  • ubuntu16.04 python2.7 cuda10.0 安装pytorch1.1.0 torchvision0.3.0

    工欲善其事 xff0c 必先利其器 显卡驱动版本和cuda版本 xff1f 今天两台电脑训练时发现速度比平时慢了 xff0c 以为是网络的变大导致 但nvidia smi发现显存占用少 xff0c gpu速度占用1 xff0c 同时用gno
  • 强化学习: 参数化动作空间环境gym-platform(1)

    gym platform环境安装 前提 xff1a 已经安装里gym 主页 xff1a https github com cycraig gym platform 安装 xff1a git clone https github com cy
  • 三步使用Docker容器创建RoboCup仿真2D环境

    本文相关视频 xff1a 三步使用Docker容器创建RoboCup仿真2D环境 哔哩哔哩 bilibili RoboCup是机器人足球世界杯 xff0c 最早于1997年在日本名古屋举办 xff0c 有来自全世界38支球队参加仿真和机器人
  • Robocup 仿真2D 学习笔记(四)阵型编辑

    一 阵型文件介绍 阵型文件里设置的是球员在比赛中的跑位点 基于helios base的阵型文件 xff0c 在目录 src formations dt中 阵型的调用在 src strategy cpp 文件 xff1a before kic
  • 2019中科大计算机考研初试经验总结

    能够在2019年290万考研大军中幸存 xff0c 不仅有努力还有运气 中科大大数据学院计算机专业上岸后 xff0c 总结一些快一年考研路上的经验和弯路 xff0c 希望可以对学弟学妹有所帮助 基础情况 xff1a 毕业工作半年辞职考研 x
  • C语言a+++b的问题

    有时候有这道面试题 xff1b int a 61 5 b 61 7 c c 61 a 43 43 43 b 在VC 43 43 里面看一下 xff1b 输出 xff1b 首先 a 43 43 43 b 和a 43 43 43 b俩个表达式概
  • Robocup 仿真2D 学习笔记(一) ubuntu16.04 搭建 robocup 仿真2D环境

    前言 robocup2D 是一个仿真机器人足球比赛 xff0c 也是一个研究多智能体强化学习等机器学习理论算法的优秀平台 xff0c 在接下来的一段时间 xff0c 通过学习如何在robocup2D仿真比赛中运用机器学习算法 xff0c 提
  • Vue - v-for 中为什么不能用 index 作为 key

    目录 1 先来看一个例子 2 上述问题的解决方法 3 高效的 Diff 算法原理 4 不能用 index 作为 key 这是一篇脱坑日记 xff0c 在做项目的过程中 xff0c 我使用了 v for 渲染子组件时 xff0c 并将 ind