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...

相关推荐
11054654018 分钟前
37、需求预测与库存优化 (快消品) - /供应链管理组件/fmcg-inventory-optimization
前端·信息可视化·数据分析·js
nunumaymax31 分钟前
在图片没有加载完成时设置默认图片
前端
OEC小胖胖1 小时前
【React 设计模式】受控与非受控:解构 React 组件设计的核心模式
前端·react.js·设计模式·前端框架·web
你怎么知道我是队长1 小时前
C语言---编译的最小单位---令牌(Token)
java·c语言·前端
一枚前端小能手2 小时前
🔥 Vue状态管理越写越乱,Pinia拯救了我
前端
cloudcruiser2 小时前
Apache HTTP Server:深入探索Web世界的磐石基石!!!
前端·其他·http·apache
一个专注api接口开发的小白2 小时前
手把手教程:使用 Postman 测试与调试淘宝商品详情 API
前端·数据挖掘·api
织_网3 小时前
Electron 核心 API 全解析:从基础到实战场景
前端·javascript·electron
vvilkim3 小时前
深入理解 Spring Boot Starter:简化依赖管理与自动配置的利器
java·前端·spring boot
艾小码3 小时前
前端安全防护手册:对抗XSS、CSRF、点击劫持等攻击
前端·安全·xss