Vue中key的作用示例代码详解


Posted in Javascript onJune 10, 2020

Vue中key的作用

key的特殊attribute主要用在Vue的虚拟DOM算法,在新旧Nodes对比时辨识VNodes。如果不使用keyVue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改、复用相同类型元素的算法,而使用key时,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。此外有相同父元素的子元素必须有独特的key,重复的key会造成渲染错误。

描述

首先是官方文档的描述,当Vue正在更新使用v-for渲染的元素列表时,它默认使用就地更新的策略,如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时DOM状态的列表渲染输出,例如表单输入值。为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute,建议尽可能在使用v-for时提供key attribute,除非遍历输出的DOM内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

简单来说,当在列表循环中使用key时,需要使用key来给每个节点做一个唯一标识,diff算法就可以正确的识别此节点,找到正确的位置直接操作节点,尽可能地进行重用元素,key的作用主要是为了高效的更新虚拟DOM。此外,使用index作为key是并不推荐的做法,其只能保证Vue在数据变化时强制更新组件,以避免原地复用带来的副作用,但不能保证最大限度的元素重用,且使用index作为key在数据更新方面和不使用key的效果基本相同。

示例

首先定义一个Vue实例,渲染四个列表,分别为简单列表与复杂列表,以及其分别携带key与不携带key时对比其更新渲染时的速度,本次测试使用的是Chrome 81.0,每次在Console执行代码时首先会进行刷新重新加载界面,避免浏览器以及Vue自身优化带来的影响。

<!DOCTYPE html>
<html>
<head>
 <title>Vue</title>
</head>
<body>
 <div id="app">
 <ul>
 <li v-for="item in simpleListWithoutKey" >{{item}}</li>
 </ul>

 <ul>
 <li v-for="item in simpleListWithKey" :key="item" >{{item}}</li>
 </ul>

 <ul>
 <li v-for="item in complexListWithoutKey">
 <span v-for="value in item.list" v-if="value > 5">{{value}}</span>
 </li>
 </ul>

 <ul>
 <li v-for="item in complexListWithKey" :key="item.id">
 <span v-for="value in item.list" :key="value" v-if="value > 5">{{value}}</span>
 </li>
 </ul>

 </div>
</body>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script type="text/javascript">
 var vm = new Vue({
 el: '#app',
 data: {
 simpleListWithoutKey: [1, 2, 3, 4, 5, 6],
 simpleListWithKey: [1, 2, 3, 4, 5, 6],
 complexListWithoutKey:[
 {id: 1, list: [1, 2, 3]},
 {id: 2, list: [4, 5, 6]},
 {id: 3, list: [7, 8, 9]}
 ],
 complexListWithKey:[
 {id: 1, list: [1, 2, 3]},
 {id: 2, list: [4, 5, 6]},
 {id: 3, list: [7, 8, 9]}
 ],
 }
 })
</script>
</html>

简单列表

在简单列表的情况下,不使用key可能会比使用key的情况下在更新时的渲染速度更快,这也就是官方文档中提到的,除非遍历输出的DOM内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。在下面的例子中可以看到没有key的情况下列表更新时渲染速度会快,当不存在key的情况下,这个列表直接进行原地复用,原有的节点的位置不变,原地复用元素,将内容更新为5678910,并添加了1112两个节点,而存在key的情况下,原有的1234节点被删除,56节点保留,添加了789101112六个节点,由于在DOM的增删操作上比较耗时,所以表现为不带key的情况下速度更快一些。

// 没有key的情况下
console.time();
vm.simpleListWithoutKey = [5, 6, 7, 8, 9, 10, 11, 12];
vm.$nextTick(() => console.timeEnd());
// default: 2.193056640625ms
// 存在key的情况下
console.time();
vm.simpleListWithKey = [5, 6, 7, 8, 9, 10, 11, 12];
vm.$nextTick(() => console.timeEnd());
// default: 3.2138671875ms

原地复用可能会带来一些副作用,文档中提到原地复用这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时DOM状态的列表渲染输出,例如表单输入值。在不设置key的情况下,元素中没有与数据data绑定的部分,Vue会默认使用已经渲染的DOM,而绑定了数据data的部分会进行跟随数据渲染,假如操作了元素位置,则元素中未绑定data的部分会停留在原地,而绑定了data的部分会跟随操作进行移动,在下面的例子中首先需要将两个A之后的输入框添加数据信息,这样就制作了一个临时状态,如果此时点击下移按钮,那么不使用key的组中的输入框将不会跟随下移,且B到了顶端并成为了红色,而使用key的组中会将输入框进行下移,且A依旧是红色跟随下移。

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width">
 <title>就地复用</title>
</head>
<body>

 <div id="app">
 <h3>采用就地复用策略(vuejs默认情况)</h3>
 <div v-for='(p, i) in persons'>
 <span>{{p.name}}<span> 
 <input type="text"/> 
 <button @click='down(i)' v-if='i != persons.length - 1'>下移</button>
 </div> 

 <h3>不采用就地复用策略(设置key)</h3>
 <div v-for='(p, i) in persons' :key='p.id'>
 <span>{{p.name}}<span> 
 <input type="text"/> 
 <button @click='down(i)' v-if='i != persons.length - 1'>下移</button>
 </div>

 </div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
 <script>
 new Vue({
 el: '#app',
 data: {
 persons: [
 { id: 1, name: 'A' },
 { id: 2, name: 'B' },
 { id: 3, name: 'C' }
 ]
 },
 mounted: function(){
 // 此DOM操作将两个A的颜色设置为红色 主要是为了演示原地复用
 document.querySelectorAll("h3 + div > span:first-child").forEach( v => v.style.color="red");
 },
 methods: {
 down: function(i) {
 if (i == this.persons.length - 1) return;
 var listClone = this.persons.slice();
 var one = listClone[i];
 listClone[i] = listClone[i + 1];
 listClone[i + 1] = one;
 this.persons = listClone;
 }
 }
 });
 </script>
</body>
</html>
<!-- 源于 https://www.zhihu.com/question/61078310 @霸都丶傲天 有修改-->

复杂列表

使用key不仅能够避免上述的原地复用的副作用,且在一些操作上可能能够提高渲染的效率,主要体现在重新排序的情况,包括在中间插入和删除节点的操作,在下面的例子中没有key的情况下重新排序会原地复用元素,但是由于v-if绑定了data所以会一并进行操作,在这个DOM操作上比较消耗时间,而使用key得情况则直接复用元素,v-if控制的元素在初次渲染就已经决定,在本例中没有对其进行更新,所以不涉及v-ifDOM操作,所以在效率上会高一些。

console.time();
vm.complexListWithoutKey = [
 {id: 3, list: [7, 8, 9]},
 {id: 2, list: [4, 5, 6]},
 {id: 1, list: [1, 2, 3]},
 ];
vm.$nextTick(() => console.timeEnd());
vm.$nextTick(() => console.timeEnd());
// default: 4.100244140625ms
console.time();
vm.complexListWithKey = [
 {id: 3, list: [7, 8, 9]},
 {id: 2, list: [4, 5, 6]},
 {id: 1, list: [1, 2, 3]},
 ];
vm.$nextTick(() => console.timeEnd());
// default: 3.016064453125ms

每日一题

总结

到此这篇关于Vue中key的作用的文章就介绍到这了,更多相关vue key 作用内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
解析使用JS 清空File控件的路径值
Jul 08 Javascript
如何用JavaScript定义一个类
Sep 12 Javascript
JS处理json日期格式化问题
Oct 01 Javascript
理解javascript封装
Feb 23 Javascript
Sortable.js拖拽排序使用方法解析
Nov 04 Javascript
使用base64对图片的二进制进行编码并用ajax进行显示
Jan 03 Javascript
加载 vue 远程代码的组件实例详解
Nov 20 Javascript
Vue2.0子同级组件之间数据交互方法
Feb 28 Javascript
Vue用v-for给src属性赋值的方法
Mar 03 Javascript
微信小程序之自定义组件的实现代码(附源码)
Aug 02 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
Feb 26 Javascript
vue实现的上拉加载更多数据/分页功能示例
May 25 Javascript
使用JavaScript获取扫码枪扫描得到的条形码的思路代码详解
Jun 10 #Javascript
js 获取扫码枪输入数据的方法
Jun 10 #Javascript
使用Vue Composition API写出清晰、可扩展的表单实现
Jun 10 #Javascript
使用 UniApp 实现小程序的微信登录功能
Jun 09 #Javascript
详解vue高级特性
Jun 09 #Javascript
vue实例的选项总结
Jun 09 #Javascript
微信小程序中的列表切换功能实例代码详解
Jun 09 #Javascript
You might like
搜索引擎技术核心揭密
2006/10/09 PHP
php学习之 循环结构实现代码
2011/06/09 PHP
PHP汉字转换拼音的函数代码
2015/12/30 PHP
php中的登陆login实例代码
2016/06/20 PHP
详解thinkphp实现excel数据的导入导出(附完整案例)
2016/12/29 PHP
PHP实现抽奖功能实例代码
2020/06/30 PHP
随机显示经典句子或诗歌的javascript脚本
2007/08/04 Javascript
javascript prototype 原型链
2009/03/12 Javascript
javaScript 判断字符串是否为数字的简单方法
2009/07/25 Javascript
高亮显示web页表格行的javascript代码
2010/11/19 Javascript
jquery 表格的增行删行实现思路
2013/03/21 Javascript
jQuery实现的一个自定义Placeholder属性插件
2014/08/11 Javascript
javascript删除数组重复元素的方法汇总
2015/06/24 Javascript
jquery实现带渐变淡入淡出并向右依次展开的多级菜单效果实例
2015/08/22 Javascript
浅谈jquery的html方法里包含特殊字符的处理
2016/11/30 Javascript
利用jquery禁止外层滚动条的滚动
2017/01/05 Javascript
利用Plupload.js解决大文件上传问题, 带进度条和背景遮罩层
2017/03/15 Javascript
JavaScript实现简单评论功能
2017/08/17 Javascript
nodejs+express搭建多人聊天室步骤
2018/02/12 NodeJs
详解vuex commit保存数据技巧
2018/12/25 Javascript
Flutter部件内部状态管理小结之实现Vue的v-model功能
2019/06/11 Javascript
python在windows和linux下获得本机本地ip地址方法小结
2015/03/20 Python
python实现在sqlite动态创建表的方法
2015/05/08 Python
Python中Class类用法实例分析
2015/11/12 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
树莓派3 搭建 django 服务器的实例
2019/08/29 Python
Python实现线性插值和三次样条插值的示例代码
2019/11/13 Python
wxPython实现文本框基础组件
2019/11/18 Python
python实现扫雷游戏
2020/03/03 Python
pandas参数设置的实用小技巧
2020/08/23 Python
解决PyCharm不在run输出运行结果而不是再Console里输出的问题
2020/09/21 Python
python实现猜拳游戏项目
2020/11/30 Python
最受欢迎的自我评价
2013/12/22 职场文书
酒店端午节活动方案
2014/08/26 职场文书
js 数组 fill() 填充方法
2021/11/02 Javascript
SQL SERVER触发器详解
2022/02/24 SQL Server