一.key在v-if和v-else中的作用
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 导包 -->
<script src="./vue.js"></script>
</head>
<body>
<!-- HTML结构 -->
<div id="app">
<button v-text="'点我更换登入方式'" @click="flag=!flag" ></button>
<div v-if="flag" v-key="phone">
手机号:<input type="text" placeholder="请输入手机号">
<br>
验证码:<input type="text" placeholder="请输入验证码">
</div>
<div v-else>
邮箱:<input type="text" placeholder="请输入邮箱">
<br>
密码:<input type="text" placeholder="请输入邮箱密码">
</div>
</div>
<script>
var app = new Vue({
//el:挂载点
el: '#app',
//data: 要渲染的数据
data: {
flag:true,
}
})
</script>
</body>
</html>
我们先把这行代码的 :key="phone"去掉 然后输入手机号和验证码 当我点击 ‘点我更换按钮时’ 我们会神奇的发现,上次输入的值依然存在与input表单框中
这是为什么呢?
这是因为vue中存在diff算法的原因 算法原理我已经在上一个篇章中进行了简单的梳理
deff算法会将新的 虚拟DOM 和旧的 虚拟DOM 进行对比 找出差异 将差异封装为补丁 通过方法的循环遍历然后打到 真实DOM 上,以差异化最小的代价去操作DOM 无差异的地方将会得到复用
当我还未点击’更换按钮’时,此时
<div v-if="flag" v-key="phone">
手机号:<input type="text" placeholder="请输入手机号">
<br>
验证码:<input type="text" placeholder="请输入验证码">
</div>
会生成一个真实的DOM(也就是渲染到页面的DOM)该真实DOM会在vue中生成一个以JS对象形式的虚拟DOM,我们称他为“旧的虚拟DOM”当我点击‘切换按钮’时
<div v-if="!flag">
邮箱:<input type="text" placeholder="请输入邮箱">
<br>
密码:<input type="text" placeholder="请输入邮箱密码">
</div>
该模块中有 文本内容 和标签属性值被改变 所以也就形成了新的虚拟DOM 我们称他为“新的虚拟DOM”
新的虚拟DOM和旧的虚拟DOM进行对比 把修改的内容作为补丁 缝到真实的DOM中 (这也就实现了少量的DOM操作渲染一个新的模块效果)
但在新旧虚拟DOM对比时 无论是新的还是旧的虚拟DOM中 标签节点中都没有value这个属性的存在 所以也就在缝补过程中 会复用之前输入的内容
那怎么解决这个这个复用问题呢?
这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key attribute 即可
([vue官方语术:Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。因此在上面的代码中点击‘切换’将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder])
二.key在v-for中的作用
我们先看一下这个代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
<p v-for="item in items" :key="item">{{item}}</p>
</div>
<script>
const app=new Vue({
el:'#app',
data:{items:['a','b','c','d','e']},
mounted() {
setTimeout(()=>{
this.items.splice(2,0,'f')
},2000);
},
})
</script>
</body>
</html>
首先我要说说明一下 :key 后面的值不建议写 下标index 因为在我们还未进行添加操作时 旧的虚拟节点中 如下
key=0 对应是 a
key=1 对应是 b
key=2 对应是 c
key=3 对应是 d
key=4 对应是 e
当我们添加时 对数据修改 此时生成一个新的虚拟节点 如下
key=0 对应是 a
key=1 对应是 b
key=2 对应是f
key=3 对应是 c
key=4 对应是 d
key=5 对应是 e
vue中规定 key是确立一个元素是完全独立的
key=2 对应f元素 和c元素
key=3 对应d元素 和c元素 …
这样会造成 key值混乱 完全违背了vue设置key值的初衷 所以不建议
接下里我们回归正题
继续说key值在v-for中的作用 如果不写key vue会根据diff算法 根据新旧DOM的差异封装补丁 对真实DOM进行打补丁的过程 比如该案例 在我没有写key的情况 在新旧DOM中 b元素 往后的其他元素他们标签他们相同所以默认为是同一个虚拟节点 只有文本不同 所以将文本封装为补丁(最后还多一个p标签 也将这个差异封装为补丁) 打在真实DOM实现页面渲染
注意只看他打节点的部分 此处是。。没加KEY值。。节点操作 直接跳转到 11 :10秒位置 注意c的变化 注意c的变化 注意c的变化 以及下面元素的变化
注意只看他打节点的部分 此处是。。没加KEY值。。节点操作 直接跳转到 11 :10秒 位置 注意c的变化 注意c的变化 注意c的变化 以及下面元素的变化
直接跳转 13:00 看他打节点操作 此处是加了KEY值的操作 注意观察页面渲染
直接跳转 13:00 看他打节点操作 此处是加了KEY值的节点操作
1.我们可以清晰看到 没加key值 相当于对 真实DOM进行了4(反正是多次 不用纠结 0.0)次DOM操作
inne(修改文本内容为f)
inne(修改文本内容c)
inne(修改文本内容d)
创creat(创建)p标签 append(插入)p标签 inne(修改文本内容e)
2.我们可以清晰看到 没加key值 相当于对 真实DOM进行了1(反正是没几次 不用纠结 0.0)次DOM操作
通过key值 明确了要添加补丁 打在真实DOM的位置
创creat(创建)p标签 append(插入)p标签 inne(修改文本内容为f)
总结
1、 key的作用主要是为了高效的更新虛拟DOM,其原理是vue在patch(补丁)过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
2、 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug。如某行数据不该更新的却更新了。
3、vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。