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 相关文章推荐
jquery中this的使用说明
Sep 06 Javascript
jQuery中(function(){})()执行顺序的理解
Mar 05 Javascript
window.close(); 关闭浏览器窗口js代码的总结介绍
Jul 14 Javascript
jQuery实现根据滚动条位置加载相应内容功能
Jul 18 Javascript
JS表格组件BootstrapTable行内编辑解决方案x-editable
Sep 01 Javascript
JS调用某段SQL语句的方法
Oct 20 Javascript
使用bat打开多个cmd窗口执行gulp、node
Feb 17 Javascript
80%应聘者都不及格的JS面试题
Mar 21 Javascript
Vue 中对图片地址进行拼接的方法
Sep 03 Javascript
JavaScript实现Tab选项卡切换
Feb 13 Javascript
Openlayers学习之地图比例尺控件
Sep 28 Javascript
JavaScript高级程序设计之变量与作用域
Nov 17 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
在Zeus Web Server中安装PHP语言支持
2006/10/09 PHP
PHP Zip解压 文件在线解压缩的函数代码
2010/05/26 PHP
JS关闭窗口与JS关闭页面的几种方法小结
2013/12/17 Javascript
BootStrap创建响应式导航条实例代码
2016/05/31 Javascript
EasyUI Combobox设置默认值 获取text的方法
2016/11/28 Javascript
javascript中json基础知识详解
2017/01/19 Javascript
Vue的事件响应式进度条组件实例详解
2018/02/04 Javascript
Vue中的异步组件函数实现代码
2018/07/20 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
2018/11/26 Javascript
js实现黑白div块画空心的图形
2018/12/13 Javascript
vue axios重复点击取消上一次请求封装的方法
2019/06/19 Javascript
移动端吸顶fixbar的解决方案详解
2019/07/17 Javascript
es6中reduce的基本使用方法
2019/09/10 Javascript
nodejs一个简单的文件服务器的创建方法
2019/09/13 NodeJs
ES6中let、const的区别及变量的解构赋值操作方法实例分析
2019/10/15 Javascript
vue列表数据发生变化指令没有更新问题及解决方法
2020/01/16 Javascript
深入解析Python中的descriptor描述器的作用及用法
2016/06/27 Python
Python程序员面试题 你必须提前准备!
2018/01/16 Python
python 3.6.4 安装配置方法图文教程
2018/09/18 Python
python3爬虫中多线程的优势总结
2020/11/24 Python
Pycharm 跳转回之前所在页面的操作
2021/02/05 Python
HTML5 canvas实现雪花飘落特效
2016/03/08 HTML / CSS
什么是ESB?请介绍一下ESB?
2015/05/27 面试题
有模特经验的简历自我评价
2013/09/19 职场文书
物流管理应届生求职信
2013/11/07 职场文书
幼儿园教师岗位职责
2014/03/17 职场文书
党员岗位承诺口号大全
2014/03/28 职场文书
新品发布会主持词
2014/04/02 职场文书
解放思想演讲稿
2014/09/11 职场文书
四风个人对照检查材料思想汇报
2014/09/25 职场文书
冲出亚马逊观后感
2015/06/03 职场文书
《思路决定出路》读后感3篇
2019/12/11 职场文书
goland 设置project gopath的操作
2021/05/06 Golang
详解Java线程池是如何重复利用空闲线程的
2021/06/26 Java/Android
一文弄懂MySQL中redo log与binlog的区别
2022/02/15 MySQL
DE1107机评
2022/04/05 无线电