详解vue中v-for的key唯一性


Posted in Vue.js onMay 15, 2021

1. DOM Diff

要想真正了解 key 属性的存在意义,还真得从 DOM Diff 说起,并不需要深入了解 DOM Diff 的原理,而是仅仅需要知道 DOM Diff 的工作过程即可。

Vue 和 React 都采用了运用虚拟 DOM 的方式减少浏览器不必要的渲染。由于 Vue 和 React 采用的都是 v = render( m ) 的方式渲染视图的,当 model 数据发生变化时,视图更新的方式就是重新 render DOM 元素。但是有时候我们只是改变了一个组件中的某一个 div 中的数据,如果采用原生 render 的方式去更新视图的话,那整个组件都要更新。岂不浪费时间?

我们日常生活中碰到这样的情况可不会全部更新,就像一个拼图拼好了,后来其中一小块需要更换,我们找到拿一块直接替换就好了,绝不会说再从头拼一次。Vue 和 React 的开发者也是这样想的,就去想方设法优化。

我们人眼一眼就可以看出改变前和改变后的不同之处,只更新不同之处就可以了。但计算机可一眼看不出来,它必须从头一块快地对比,直至找到不同之处进行更新。这个将改变前和改变后进行对比找不同的过程就是 DOM Diff,DOM Diff 中的 DOM 是虚拟 DOM,也就是 JavaScript 对象,一一比较找到不同之处后,就去局部更新真正的 DOM。

在比较的过程中, 虚拟 DOM 也会构成一棵虚拟 DOM 树,DOM Diff 的工作过程就是比较两棵虚拟 DOM 树上的对象节点,具体就是每一层和每一层的对应位置进行比较。正是因为计算机只会比较每一层对应位置的的两个虚拟 DOM 元素,如果这两棵树中改变后的树的某一层只是插入了一个节点,那树的结构是不变的,DOM Diff 在比较这一层的时候就会导致错位比较了,如下图所示:

详解vue中v-for的key唯一性

因为这一层的虚拟 DOM 节点对于 Vue 和 React 来说除了 DOM 节点本身外是完全没有任何不同的,所以 DOM Diff 在比较的时候就只能按照对应位置一一比较了。

一一比较后,如果节点类型相同,那么就会复用该节点,单单局部更新该节点内不同的内容处。就像上述图中的,如果这是 ul 下的 li 的虚拟 DOM 节点的话,那一一比较后发现节点类型相同,就复用之前的节点,将节点里面的内容进行改变,也就是,将C更新成F,D更新成C,E更新成D,最后再插入E。

上述是插入节点的情况,带来的后果就是效率上的降低,但如果是删除节点的情况,那带来的后果可就不仅仅是效率了。

假如是点击一个按钮删除一个 li 元素,那新旧虚拟 DOM 树进行比较的时候,还是根据树中每一层的对应位置一一比较,比如删除后的 [1,2,3] 变成了 [1,3],它就会将第一个 li 和第二个 li 相比较,发现元素类型没有变化,就会复用第一个 li,再递归对比 li 里面的,发现都没变化就继续复用。到了第二个 li 之间比较的时候,发现也都是 li 元素,那就会复用之前的li,单单将 2 变成了 3。

此时,如果复用的 li 中有子元素的话,子元素依赖的数据没有发生变化的话,就会继续复用之前的子组件,这样就会导致一个错位,如下图:

详解vue中v-for的key唯一性

2. 为同一层的相同类型的元素添加 key 属性

在上述的 DOM Diff 算法中,比较的仅仅是两棵树同一层的对应位置,在不同层之前的元素之间是不需要比较的,而且,当 DOM Diff 的过程中发现,改变后的虚拟 DOM 和之前的虚拟 DOM 类型不同的时候,就会将之前的卸载,重新再添加改变后的元素节点。因此,上述的问题就出现在,两棵树中同一层的节点类型相同时,在该层添加或删除时会降低效率或者带来 bug。

这就是我们在 v-for 循环中生成同种类型的标签元素时的情况,如果不为该标签节点做点什么,就存在bug隐患,那么应该做什么呢?

答案就是为同一层的相同节点类型的节点添加一个唯一标识的 key 值,这样,在 DOM Diff 进行配对比较时,就会将 key 相同的两个虚拟 DOM 进行比较,而不是仅仅按照对应位置进行比较了。

这样一来就不会导致错位比较了,就大大提高了比较的效率,解决了 bug 隐患。

3. key 不能是 index 下标值

因为数组或对象的 index 下标值是唯一的,因此我们经常使用 index 作为 key 属性的值,有的人说这样是可以的,会带来性能上的优化什么的,但使用 index 下标值是会有大大的 bug 隐患的。

这些 bug 会在你 v-for 循环的数组或对象发生添加或删除或顺序改变时。

那么为什么不能使用 index 下标呢?

其实就是因为 index 下标使用了跟没使用了一样,因为在添加和删除时,某一个特定元素的 index 是会变的,比如 [1,2,3] 变成了 [1,3] 后,原来数据 3 对应的下标为2,删除后数据 3 的下标变成了 1,这在 DOM Diff 的时候,会根据 key 值相等的进行两两配对比较,这数据3对应的节点前后还是对应不上,因此,使用了 index 作为 key 跟没设置 key 是一样的效果。

这就是为什么不要使用 index 作为 key 的原因。

因此:key 属性值必须是独一无二的且不会改变的

以上就是详解vue中v-for的key唯一性的详细内容,更多关于vue中v-for的key唯一性的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
Dec 04 Vue.js
vue监听滚动事件的方法
Dec 21 Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 Vue.js
vue项目中openlayers绘制行政区划
Dec 24 Vue.js
详解Vue2的diff算法
Jan 06 Vue.js
vue-router路由懒加载及实现的3种方式
Feb 28 Vue.js
如何理解Vue前后端数据交互与显示
May 10 Vue.js
Vue接口封装的完整步骤记录
May 14 Vue.js
vue-cli4.5.x快速搭建项目
May 30 Vue.js
vue修饰符.capture和.self的区别
Apr 22 Vue.js
vue实现登陆页面开发实践
May 30 Vue.js
vue本地构建热更新卡顿的问题“75 advanced module optimization”完美解决方案
Aug 05 Vue.js
解读Vue组件注册方式
May 15 #Vue.js
如何理解Vue简单状态管理之store模式
May 15 #Vue.js
Vue如何实现组件间通信
May 15 #Vue.js
详解Vue的sync修饰符
May 15 #Vue.js
深入理解Vue的数据响应式
May 15 #Vue.js
详解Vue的options
May 15 #Vue.js
vue实现无缝轮播效果(跑马灯)
May 14 #Vue.js
You might like
php流量统计功能的实现代码
2012/09/29 PHP
2个比较经典的PHP加密解密函数分享
2014/07/01 PHP
PHP is_array() 检测变量是否是数组的实现方法
2016/06/13 PHP
检测是否已安装 .NET Framework 3.5的js脚本
2009/02/14 Javascript
JavaScript 异步调用框架 (Part 1 - 问题 & 场景)
2009/08/03 Javascript
jquery图片上下tab切换效果
2011/03/18 Javascript
js拦截alert对话框另类应用
2013/01/16 Javascript
js判断当页面无法回退时关闭网页否则就history.go(-1)
2014/08/07 Javascript
jQuery实现自动滚动到页面顶端的方法
2015/05/22 Javascript
freemarker判断对象是否为空的方法
2015/08/13 Javascript
JavaScript实现汉字转换为拼音的库文件示例
2016/12/22 Javascript
详解javascript中对数据格式化的思考
2017/01/23 Javascript
微信小程序 基础知识css样式media标签
2017/02/15 Javascript
js基于FileSaver.js 浏览器导出Excel文件的示例
2017/08/15 Javascript
9种使用Chrome Firefox 自带调试工具调试javascript技巧
2017/12/22 Javascript
React.js组件实现拖拽排序组件功能过程解析
2020/04/27 Javascript
JavaScript, select标签元素左右移动功能实现
2020/05/14 Javascript
Python实现类似jQuery使用中的链式调用的示例
2016/06/16 Python
浅谈python装饰器探究与参数的领取
2017/12/01 Python
CentOS6.9 Python环境配置(python2.7、pip、virtualenv)
2019/05/06 Python
详解python websocket获取实时数据的几种常见链接方式
2019/07/01 Python
调用其他python脚本文件里面的类和方法过程解析
2019/11/15 Python
pytorch绘制并显示loss曲线和acc曲线,LeNet5识别图像准确率
2020/01/02 Python
python对 MySQL 数据库进行增删改查的脚本
2020/10/22 Python
python selenium 获取接口数据的实现
2020/12/07 Python
5分钟实现Canvas鼠标跟随动画背景
2019/11/18 HTML / CSS
非洲NO.1网上商店:Jumia肯尼亚
2016/08/18 全球购物
日本著名的平价时尚女性购物网站:Fifth
2016/08/24 全球购物
总经理岗位职责描述
2014/02/08 职场文书
社区消防工作实施方案
2014/03/21 职场文书
2015纪念九一八事变84周年演讲稿
2015/03/19 职场文书
2016年最美孝心少年事迹材料
2016/02/26 职场文书
祝福语集锦:给百岁老人祝寿贺词
2019/11/19 职场文书
MySQL中出现乱码问题的终极解决宝典
2021/05/26 MySQL
只用50行Python代码爬取网络美女高清图片
2021/06/02 Python
Html5获取用户当前位置的几种方式
2022/01/18 HTML / CSS