Vue文档中关于v-for指南模块有这么一段话:
image.png

那什么是就地更新策略呢?我们先来写一个案例,通过案例来介绍。

  1. const app = {
  2. template: `
  3. <ul>
  4. <li v-for="(item, index) of list">
  5. <span>{{ item.value }}</span>
  6. <button @click="delteItem(index)">Del</button>
  7. </li>
  8. </ul>`,
  9. data(){
  10. return{
  11. list: [
  12. {
  13. id: 1,
  14. value: "item-1"
  15. },
  16. {
  17. id: 2,
  18. value: "item-2"
  19. },
  20. {
  21. id: 3,
  22. value: "item-3"
  23. }
  24. ]
  25. }
  26. },
  27. methods: {
  28. delteItem(index) {
  29. this.list.splice(index, 1);
  30. }
  31. }
  32. }

以上代码,实现了一个简单的列表,然后通过按钮可以进行删除某项,效果如下:
image.png

点击删除 DOM 的变化

当我们删除item-2的时候看看会发生什么?
屏幕录制2023-02-17 14.38.55.gif
从上面的动态图中我们可以看到:我们删除的是**item-2****li**,第二个**li**里面的内容变成了**item-3**的内容,实际上删除的是第三个**li**(浏览器中紫色闪烁表示**dom**的内容发生了变化)。

这就是Vue的就地更新策略,回头看Vue文档的那句话:
image.png :::info Vue将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。
也就是说默认的情况下Vue会尽量使用已经存在的DOM元素,直接在已有的DOM上进行复用修改,这样可以带来一定性能上的提升。 :::

看一个更明显的案例:

  1. const app = {
  2. template: `
  3. <div>
  4. <div v-if="isLogin">
  5. <span>欢迎</span>
  6. <a href="javascript:;" @click="isLogin = false">xiechen</a>
  7. </div>
  8. <div v-else>
  9. <a href="javascript:;" @click="isLogin = true">登录</a>
  10. <a href="javascript:;">注册</a>
  11. </div>
  12. </div>`,
  13. data(){
  14. return{
  15. isLogin: false
  16. }
  17. }
  18. }

以上代码,两个模块里都有a标签,按照我们的理解当条件发生变化的时候,div都会重新渲染,实际上Vue会进行就地更新策略。
屏幕录制2023-02-17 14.53.11.gif
可以很明显的看到第二个a标签一直在闪烁,也就是内容一直在更新而不是销毁重新创建的a标签!!!

就地更新的缺陷

再次回到Vue文档的那句话:
image.png
这又是什么意思呢?同样的我们还是用动态图要进行演示,我们把第一个案例进行改造,给每个li都新增一个input输入框:

  1. <ul>
  2. <li v-for="(item, index) of list">
  3. <span>{{ item.value }}</span>
  4. <input type="text" />
  5. <button @click="delteItem(index)">Del</button>
  6. </li>
  7. </ul>

屏幕录制2023-02-17 15.02.03.gif
实际的效果有点让你疑惑,我明明删除的是item-2,为什么item-3的输入框内容却发生了变化? :::info 这其实就是「就地更新」的缺陷!!!
因为input输入框是一个临时的状态,Vue无法判断节点的value到底有什么用,所以在删除item2的时元素会进行复用,input里的值也是会被保留的,实际删除的是第三个li及第三个li内的input。 :::

如何进行解决?

现在的问题就是Vue不会根据最新的顺去更新DOM,而是用已有的DOM进行属性的修改。
在这种情况下,我们需要给li标签绑定一个key属性,这样Vue就会根据最新的数据对DOM进行调整,而它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。
可以简单认为key是给每一个DOM节点一个唯一标识,这样Vue就不会启用就地更新了。

  1. <ul>
  2. <li v-for="(item, index) of list" :key="item.id">
  3. <span>{{ item.value }}</span>
  4. <input type="text" />
  5. <button @click="delteItem(index)">Del</button>
  6. </li>
  7. </ul>

屏幕录制2023-02-17 15.22.24.gif
从图片中可以看到,加上key属性后item-2删除后,item-2input也被删除啦。

key 的作用

简单说**key**的作用主要是为了更高效的对比虚拟**DOM**中每个节点是否是相同节点;
举个简单的例子:三胞胎战成一排,你怎么知道谁是老大?如果老大皮了一下子,和老三换了一下位置,你又如何区分出来?给他们挂个牌牌,写上老大、老二、老三。这样就不会认错了,key就是这个作用。 :::info 所以在使用key属性的时候需要注意,key属性必须是唯一的,不变的!当数据更新的时候,Vue可以通过key属性去确认元素,同时确认子元素是否要进行更新。 ::: image.png