Vue的“就地更新”策略

4,110 阅读2分钟

什么是“就地更新”

官方文档给的解释:

v-for 渲染列表时,如果列表数据发生了变化,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。也就是说默认的情况下Vue会尽量使用已经存在的DOM元素,直接在已有的DOM上进行复用修改,这样可以带来一定性能上的提升。

// DOM
 <div id="app">
        <ul>
            <li v-for="(item,index) in list" :class="item.name">
                {{item.id}}
                <input type="text" :placeholder="item.name">
                <button v-on:click="deleteLi(index)">删除</button>
            </li>
        </ul>
    </div>
    
    // JS
     new Vue({
            el: '#app',
            data: {
                list: [{name:'hello',id:1},{name:'world',id:2}]
            },
            methods: {
                deleteLi(index) {
                    this.list.splice(index,1)
                }
            }
        })
  1. 有两个v-for的li,每个li里面都有一个input和一个删除按钮
  2. 在input里面手动输入两个值,然后删除第一个li 效果演示:

观察到的现象好像很奇怪,为什么删除的是第一个li,但好像把第二个li删除了?实际上是这样的变化: Vue经过对比发现了你将第一个数据删除了,即 list[0]被删除了,现在list就是这样:[{name:'world',id:2}] 所以列表只需要渲染这个数据,看效果动态图能发现,li的class和内部的属性也更新了,注意,仔细看这是在第一个li上直接更新的,也就是复用了第一个li的DOM结构,也就是直接在第一个li上改成了第二个li(观察对应的class和li前面的数字)

就地更新的问题

既然第一个li被删除了,那里面的input里的值却还在呢?这是很让人费解的事儿,其实这就是Vue的"就地复用"的一个缺陷,因为这里的input是临时DOM状态,在元素复用时,input里的值也是会被保留的。这一点,实际上官网已经提到了。

如何解决这个问题

现在的问题就是Vue不会根据最新的顺去更新DOM,而是用已有的DOM进行属性的修改,如何让Vue重新对DOM进行排列呢? 这就需要使用key属性。 只需要在li上加上 :key="item.id" 这样Vue就会根据最新的数据对DOM进行调整,而它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。 可以简单认为key是给每一个DOM节点一个唯一标识,这样Vue就不会启用就地更新了。 看修改后的效果演示:

总结:

  1. v-for在渲染列表时,默认会启用“就地更新”策略,复用元素节点,导致一些问题。
  2. 通过key属性解决这个问题