Vue中key的作用与原理

一、引言

日常开发中,我们经常会用到v-for指令,处理列表渲染等涉及动态元素增删改场景,为我们带来了遍历数组数据的便捷与高效。在使用v-for时,一个不可或缺的伙伴便是key属性,它的的作用和原理是什么呢?

二、key的作用与原理

key 作用 :用来作为节点的唯一标识

当为一个列表绑定一个key属性时,该属性会存在于虚拟的DOM中,key是虚拟DOM对象的标识,当数据发生变化时,会生成新的虚拟DOM。随后Vue会使用Diff算法进行新虚拟DOM与旧虚拟DOM的差异比较。进行比较的过程可以分成两种情况:

  • 当旧虚拟DOM中找到了与新虚拟DOM相同的key时,若虚拟DOM中内容没变, 直接使用之前的真实DOM;若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM;
  • 当旧虚拟DOM中未找到与新虚拟DOM相同的key时,创建新的真实DOM,随后渲染到到页面。

三、情景举例

举例一:在v-for中不使用key属性(vue会把默认索引作为key的唯一标识)

xml 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>key的作用以及原理</title>
		<script src="vue.js"></script>
	</head>
	<body>
		<div id="app">
			<ul>
				<li v-for="(list,index) in  userinfo">{{list.username +"-"+  list.age}}</li>
			</ul>
			<button @click="add">点击添加信息</button>     
		</div>

		<script type="text/javascript">
			const vm = new Vue({
				el: "#app",
				data: {
					msg:{username:"赵六",age:20},
					userinfo: [{
						username: "张三",
						age: 17
					}, {
						username: "李四",
						age: 18
					}, {
						username: "王五",
						age: 19
					}]
				},
				methods:{
					add()  //用来点击向userinfo头部添加一条数据
					{
						this.userinfo.unshift(this.msg)
					}
				}
			})
		</script>
	</body>
</html>

运行结果如下图:

根据运行结果,大家可以看到不写key属性也能正常地显示我们想要的效果,所以为何要多加一个key属性呢?

事实上,当你没有添加key属性时,系统会默认将绑定的key属性的值指向index(列表的索引),例如:{username:'张三',age:17}的index值就为0,{username:'李四',age:18}的index值就为1,依次类推,为每一个li标签加上标识。

举例二:在v-for中使用key属性

xml 复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>key的作用与原理</title>
       <script src="vue.js"></script>
    </head>
    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <button @click.once="add">添加新用户</button>
            <ul>
                <!-- 绑定了key属性,并将其值指向于index -->
                <li v-for="(p,index) of persons" :key="index">
                    {{p.username}}-{{p.age}}&nbsp;&nbsp;&nbsp;&nbsp;
                    <!-- 增加了input输入框 -->
                   爱好: <input type="text">
                </li>
            </ul>
        </div>
        <script type="text/javascript">
            new Vue({
                el:'#root',
                data:{
                    persons:[
                        {id:'001',username:'张三',age:17},
                        {id:'002',username:'李四',age:18},
                        {id:'003',username:'王五',age:19}
                    ]
                },
                methods: {
                    add(){
                        const p = {id:'004',username:'赵六',age:20}
                        this.persons.unshift(p)
                    }
                },
            })
        </script>
</html>

通过以上的运行结果我们可以很明显地看出问题,当爱好输入框没有内容时,添加之后并没有什么问题,但是当我们在添加新用户之前先输入现有用户的爱好时,我们会发现添加之后出现了输入框错乱的问题,为何会出现这种问题呢?

当我们把index作为key时,初始数据之后会根据数据生成虚拟的DOM,对应的li标签中的key值分别是0,1,2。当数据生成初始虚拟DOM之后,会创建新的真实DOM,随后渲染到到页面。而当我们再添加一条数据时,会根据我们新的数据来生成新的虚拟DOM。新的虚拟DOM会与初始的也就是旧的虚拟DOM来进行对比,先找到对应相同的key值的li,若虚拟DOM中内容没变, 直接使用之前的真实DOM,若虚拟DOM中内容变了, 则生成新的真实DOM。

对比发现key="0"中的内容有变化,从"张三-17"变成了"赵六-20",随后替换掉页面中之前的真实DOM。而爱好:<input type="text">对比的结果是相同的,因此保存原本的内容。key="1"以及key="2"以此类推。而key="3"并没有在旧虚拟DOM中找到,因此创建新的真实DOM,随后渲染到到页面。

举例三:在v-for中使用数据唯一标识作为key值

xml 复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>key的作用与原理</title>
       <script src="vue.js"></script>
    </head>
    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <button @click.once="add">添加新用户</button>
            <ul>
                <!-- 绑定了key属性,并将其值指向于index -->
                <li v-for="(p,index) of persons" :key="p.id">
                    {{p.username}}-{{p.age}}&nbsp;&nbsp;&nbsp;&nbsp;
                    <!-- 增加了input输入框 -->
                   爱好: <input type="text">
                </li>
            </ul>
        </div>
        <script type="text/javascript">
            new Vue({
                el:'#root',
                data:{
                    persons:[
                        {id:'001',username:'张三',age:17},
                        {id:'002',username:'李四',age:18},
                        {id:'003',username:'王五',age:19}
                    ]
                },
                methods: {
                    add(){
                        const p = {id:'004',username:'赵六',age:20}
                        this.persons.unshift(p)
                    }
                },
            })
        </script>
</html>

我们可以明显地看出,若我们的key属性绑定的值为数据中的唯一标识时,我们就能够得到我们想要的结果。对应的原理图如下:

每一个li标签中绑定的值为id,因此当添加一个新成员时,对应的key值为004,并没有在旧的虚拟DOM中找到,因此添加到真实的DOM中并渲染在页面上。其他的根据key值比较没有变化,保留原本的内容。

四、结语

若使用index作为key需要考虑如下两个问题:

  • 若对数据进行逆序添加、逆序删除等破坏顺序的操作时, 会产生没有必要的真实DOM更新 ,虽然界面效果没问题,但效率低;
  • 如果结构中还包含输入类的DOM,会产生错误DOM更新 ,界面会产生问题。

我们在开发中,首先最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号等唯一值。 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,用index作为key是没有问题的。

参考文档:v2.cn.vuejs.org/v2/api/#key

v2.cn.vuejs.org/v2/guide/li...

相关推荐
时间的情敌4 小时前
Vue3 和 Vue2 的核心区别
前端·javascript·vue.js
533_4 小时前
[antv x6] 限制节点/边是否可以移动,移动时返回父节点,限制节点的移动范围
前端
步步为营DotNet4 小时前
深入理解ASP.NET Core Middleware:构建高效Web应用的管道基石
前端·后端·asp.net
毕设十刻4 小时前
基于Vue的旅游网站yzwa8(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
数据库·vue.js·旅游
|晴 天|4 小时前
微前端架构入门:从概念到落地实践
前端·架构
一字白首4 小时前
Node.js+Vue 联动,Vue 快速上手:基础学习
vue.js·学习·node.js
悟能不能悟4 小时前
vue项目,url访问不了,用route-link跳过去就可以访问,为什么
前端·javascript·vue.js
程序媛_MISS_zhang_01104 小时前
APP中列表到详情,详情返回列表时候,返回定位到之前查看详情那条数据
前端·javascript·vue.js
还有多远.4 小时前
前端部署后自动检测更新
前端·javascript·vue.js
爱吃无爪鱼4 小时前
02-前端开发核心概念完全指南
css·vue.js·前端框架·npm·node.js·sass