key — 属性为什么要加
最后更新于:2022-04-02 08:06:18
>[success] # key 属性问什么要加
~~~
1.在我们做一些循环的时候,经常需要加key属性,即使不加程序也能运行
,那么key到底该加不加,应该用什么作为key的绑定,下面结论只代表
我个人的理解,可能有误
~~~
>[danger] ##### 官方文档对key -- api 解释
~~~
* 文档中解释:
1.'key' 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。
如果不使用 'key','Vue' 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用
相同类型元素的算法。使用 key,它会基于 'key' 的变化重新排列元素顺序,并且会移除
'key' 不存在的元素。
~~~
>[info] ## 翻译理解官方意思
~~~
1.下面的文章是参考了一下文章整理
2.下面文章中的'diff'算法笔者没有实际去看过具体原理不懂,是根据下面
几篇文章粗滤知道了大概意思,因此可能会出现部分误差
~~~
文章参考:
[简书作者--Nanshannan《VUE中演示v-for为什么要加key》](https://www.jianshu.com/p/4bd5e745ce95)
[思否作者--funnycoderstar 《为什么使用v-for时必须添加唯一的key?》](https://segmentfault.com/a/1190000013810844)
[vue官方文档解释](https://cn.vuejs.org/v2/api/#key)
[博客园作者-wind文章《详解vue的diff算法》](https://www.cnblogs.com/wind-lanyan/p/9061684.html)
[简书作者--小进进不将就《React之diff算法》](https://www.jianshu.com/p/3ba0822018cf)
[极客时间--视频Vue实战开发](https://time.geekbang.org/course/detail/163-86448)
[vue官方文档--为-v-for-设置键值-必要](https://cn.vuejs.org/v2/style-guide/?#%E4%B8%BA-v-for-%E8%AE%BE%E7%BD%AE%E9%94%AE%E5%80%BC-%E5%BF%85%E8%A6%81)
[vue官方文档--没有在-v-if-v-else-if-v-else-中使用-key-谨慎使用](https://cn.vuejs.org/v2/style-guide/?#%E6%B2%A1%E6%9C%89%E5%9C%A8-v-if-v-else-if-v-else-%E4%B8%AD%E4%BD%BF%E7%94%A8-key-%E8%B0%A8%E6%85%8E%E4%BD%BF%E7%94%A8)
>[danger] ##### 传统的diff 算法 vs vue diff 算法
~~~
1.首先解释一个名词'diff' 算法:计算出Virtual DOM中真正变化的部分,并只
针对该部分进行原生DOM操作,而非重新渲染整个页面。
2.传统的diff算法,会出现,通过循环递归对节点进行依次对比,算法复杂度
达到 O(n^3) ,n是树的节点数,这个有多可怕呢?——如果要展示1000个节
点,得执行上亿次比较
3.vue的diff 算法如上图所示:
3.1 对树分层比较,两棵树 只对同一层次节点 进行比较。如果该节点不存
在时,则该节点及其子节点会被完全删除,不会再进一步比较。
3.2 只需遍历一次,就能完成整棵DOM树的比较。
4.因此整个O(n^3)复杂度 转化为 O(n)复杂度
~~~
>[danger] ##### diff 算法已经优化了为啥还要加key
* 根据上面的描述简单勾勒出下面几个场景
~~~
1.带入一句话,就是同数量没有增加或删除,只是改变顺序是只会触发刷新
,但有新增和删除会触发新增dom 并且销毁改变的dom
2.key 还可以应用在组件上
3.文章参考中最后一篇官方文,可以注重看四五六的情况来品味
~~~
* 第一场景移动
~~~
1.'F' 'E' 是挂载到'C' 节点上,当我们想把'B','C','D' 节点进行位置上的移动
根据上面讲VUE 会比较同层的节点的改变,这样相当于只要移动'B','C','D',
而不需要,移动'B','C','D' 后再移动'E','F',因为根据vue的规则来说'E','F'没有
变化
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/319723b150ceac6b3be2b69f31b1dc6b_1228x517.png)
* 场景二新增删除
~~~
1.根据上面的总结3.1条的描述,同层节点中'C'已经被删除,因此,此时新的
节点树中的'C' 被删除,然后到下一层发现'B'节点上有'C' 节点注意此时不会
移动,因为在上一个层级'C'节点已经被删除了,所以此时会创建一个新的'C'
和'E','F'节点
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/a12c927a8a30ccabe97a4e0639559713_1211x567.png)
* 场景三删除新增
~~~
1.因此会吧'C'节点删除,并且创建'G'节点,然后再接着重新创建'E','F'节点
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/557f36da37f572353b80581cf61c5efb_1171x507.png)
* 场景四更新删除新建(三个同类型节点) 且无key
~~~
1.要注意场景四是三个同类节点,场景1说的是三个不同类,举个列子:
场景一: A 节点相当于'div',B节点相当于'span',C节点相当于'a',D节点相当于
'p标签',场景四:A节点相当于'ul','B1','B2','B3'相当于'li'
2.不仅仅是dom 也可以是组件,相同组件也相当于场景四
3.此时场景四就可以抽象理解成我们在给ul 标签中的 li 使用了 'v-for' 属性
我们拖动了'B2' 移动到了'B1'的位置,发生同级中'B2'和'B1' 进行了移动,
但是'E','F'却需要重新创建
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/c92c57527b5bd9df388359f18a2c7983_1201x456.png)
* 场景五更新删除新建(三个同类型节点) 且有key
~~~
1.要注意场景四是三个同类节点,场景1说的是三个不同类,举个列子:
场景一: A 节点相当于'div',B节点相当于'span',C节点相当于'a',D节点相当于
'p标签',场景四:A节点相当于'ul','B1','B2','B3'相当于'li'
2.不仅仅是dom 也可以是组件,相同组件也相当于场景四
3.此时场景四就可以抽象理解成我们在给ul 标签中的 li 使用了 'v-for' 属性
我们拖动了'B2' 移动到了'B1'的位置,发生同级中'B2'和'B1' 进行了移动,
但是因为有key 了,所以'E','F'就不需要创建直接跟着'B2'一起移动了
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/153f342c860da87d98ec2d1f8cc95d66_1148x472.png)
* 场景六有key 插入
~~~
1.当有key的时候会直接插入'B4'并且会复用'B2'和'B3'
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/1222ea310315de39366361741d5ddb0d_1179x331.png)
>[danger] ##### 对场景进行进一步总结
~~~
1.根据上面的场景,可以大概勾勒出v-for 针对场景四五六,因为循环出来的
肯定是想相同节点挂载,如果没有用key 就会 出现场景四的效果重新生成
新的挂载节点
2.下面的例子会更加深入说明场景六没有key 的样子
~~~
* 场景六特别说明
~~~
1.当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默
认情况下也是遵循以上原则。
比如一下这个情况:
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/b790142bec2535c6e8c3ffc2d1ed76a3_477x191.png)
~~~
1.我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/7d9f901f597e54a5cc512c84f109c7c9_572x215.png)
~~~
1.即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的
识别此节点,找到正确的位置区插入新的节点。
2.因此有了key,高效的更新虚拟DOM
~~~
>[success] # 注意点key 不要使用index
~~~
1.v-for 中的最后一个例子有实际说明使用index 作为key的一个bug,不仅如
此,使用index作为key 的时候实际整个代码是没有实现场景六的效果,
而是实现了下面的情况
~~~
* 使用index 作为key
~~~
第一个数据可以复用之前的之外,另外三条数据都需要重新渲染;
~~~
~~~
之前的数据 之后的数据
key: 0 index: 0 name: test1 key: 0 index: 0 name: test1
key: 1 index: 1 name: test2 key: 1 index: 1 name: 我是插队的那条数据
key: 2 index: 2 name: test3 key: 2 index: 2 name: test2
key: 3 index: 3 name: test3
~~~
* 使用唯一元素例如id
~~~
1.现在对比发现只有一条数据变化了,就是id为4的那条数据,因此只要新渲染
这一条数据就可以了,其他都是就复用之前的;
~~~
~~~
之前的数据 之后的数据
key: 1 id: 1 index: 0 name: test1 key: 1 id: 1 index: 0 name: test1
key: 2 id: 2 index: 1 name: test2 key: 4 id: 4 index: 1 name: 我是插队的那条数据
key: 3 id: 3 index: 2 name: test3 key: 2 id: 2 index: 2 name: test2
key: 3 id: 3 index: 3 name: test3
~~~
';