v-key指令的作用,可以说是面试中比较常见的问题了,那么现在我们来简单了解下。

1. v-key的使用场景

我们先来看一段代码:

<!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/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <p v-for="item in items" :key="item">
            {{item}}
        </p>
    </div>
</body>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            items: ['a', 'b', 'c', 'd', 'e']
        },
        mounted() {
            setTimeout(() => {
                // 在c的前面插入f
                this.items.splice(2, 0, 'f')
            }, 2000)
        },

    })
</script>

</html>

这段代码的作用是:循环遍历items中的元素,之后利用生命周期函数mounted()执行了一个定时函数,定时器的作用是在元素c的前面插入f;

代码执行之后,先在在浏览器中先遍历出了a,b,c,d,e五个元素,2秒之后就会在c之前插入f;

我们可以看到,这段代码中在p标签上添加了v-key指令。

接下来,我们就来分析一下,v-key在其中所起的作用。

2. 从源码层面看v-key的作用

源码地址:src\core\vdom\patch.js---updateChildren()

 

由于篇幅问题,我在这里只是做一个粗略的展示,有兴趣的朋友可以自己去学习。

简单说一下:updataChildren()函数是diff算法中最重要的函数,它的作用就是向下递归遍历树;diff算法则是虚拟dom的核心算法(感兴趣的朋友可以自己找资料学习,笔者已经晕倒,关于这更深层次的问题,我表示听不见,......)

言归正传;

上面提到了算法,所以,我们依据算法的执行效率来分析问题;

如果上述代码,仅仅只是循环渲染数组中的a,b,c,d,e,那么使用v-key和不使用v-key也就无任何区别,可问题是,它还执行了一个插入元素操作,此时,v-key的作用也就体现出来了

接下来,我们就分别来讨论:

1. 不使用v-key

如果不使用v-key,此时,执行完首次渲染之后,情况会是这样的:

在执行插入操作时,因为没有使用v-key,更新算法不知道到底要更新谁,所以,它只能见到元素就更新,也就是说它会从头开始遍历更新元素,这还不是最糟糕的,那最糟糕的是什么呢?

首先,插入之前存在一个老数组[a,b,c,d,e],而插入之后,又产生了一个新数组[a,b,f,c,d,e];

old---->[a,b,c,d,e]

new--->[a,b,f,c,d,e]

其实,在执行插入操作之后,它的更新顺序是这样的:先渲染a,然后是b(这没问题),当更新渲染第三个元素时,由于老数组里面是c,而新数组是f,它会直接覆盖c,然后将f渲染出来,之后依次覆盖渲染,而对于最后一个元素e,它没有执行覆盖操作,因为老数组里没有第五个元素,那么它就会直接创建一个e,然后追加渲染出来;

那么,上面这个操作更新算法一共执行了几次呢?

其实是五次,而且还有一次创建追加操作;

2. 使用v-key

那么使用v-key指令之后,它的渲染更新是怎样的呢?

依旧是新旧数组:

old---->[a,b,c,d,e]

new--->[a,b,f,c,d,e]

此时的更新顺序是先更新a,然后更新b(这都一样),之后的更新就会出现一些逻辑上的变化,什么变化呢?主要就是它不会再去覆盖更新f了,而是转而更新e(为什么会这样呢,其实是更新算法所决定的,其根本就是它在新老数组中会对比更新,因为新数组中突然出现一个f,更新就转而判断尾部新老数组同时存在的元素,然后将它们更新出来),之后就会更新d,c,此时老数组已经空了,所以更新停止,最后,f被创建出来,然后插入到指定位置。

那么,上面这个操作更新算法一共执行了几次呢?

其实是五次,而且还有一次创建追加操作;

3. 那么问题来了?

有朋友就会问,使用v-key和不适用v-key都执行了五次更新操作,和一次追加操作,难道它们的执行效率不一样吗?

可以肯定的是,执行效率它是不一样的。

不使用v-key:它的更新是在执行相应的覆盖操作,算法是确确实实的在执行,有时间效率和空间效率的考量;

使用v-key:它的更新则是在判断是否已经渲染过此元素,虽然有执行次数,但是算法其实是没有执行的,也就不存在时间效率和空间效率的考量,它仅仅只是执行了一个创建插入操作。

3. 结论

1. key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使整个patch过程更加高效,减少DOM操作量,提高性能;

2. 如果不设置key,还会在列表更新时引发一些隐蔽的bug;

3. vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

Logo

前往低代码交流专区

更多推荐