Vue列表渲染——key的原理解析

2023-01-10 14:09:52

文章目录

前言

        key的作用(主要):标识节点,以便相同的节点可以被高效复用

       要了解key的原理,就得先了解虚拟Dom的渲染过程

一、虚拟Dom渲染

        在Vue渲染过程中,Vue会先将初始数据生成虚拟Dom(内存中),生成虚拟Dom后,再通过虚拟Dom生成真实Dom(也就是我们页面上看到的数据)。此时页面存在虚拟Dom和真实Dom两个部分。

        当页面的数据发生改变时,也就是修改了Dom节点后,该页面会重新生成一个新的虚拟Dom,此时页面有新旧两个虚拟Dom树,Vue通过对新旧两个虚拟Dom进行比较,从而跟新页面数据,比较的过程使用diff算法,过程如下:

1.旧虚拟Dom中的节点找到与新虚拟Dom中具有与其相同key值的节点进行比较:

  • 若新虚拟Dom内容没变,直接复用旧虚拟Dom作为页面的真实Dom
  • 若新虚拟Dom内容改变,则生成新的真实Dom,替换掉页面之前的真实Dom

2.旧虚拟Dom的节点未找到与新虚拟Dom具有与其相同key值的节点:

  • 创建新的真实Dom,随后渲染到页面

举个栗子:

<!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>key原理</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 遍历数组 -->
        <h2>The list of persons</h2>
        <ul>
            <li v-for="(p,index) in persons" :key="p.id">
                {{p.id}}--{{p.name}}--{{p.age}}
            </li>
        </ul>
        <button @click="add">Add</button>
    </div>
    <script>
        Vue.config.productionTip = false; 
        const vm = new Vue({
            el:'#root',
            data: {
                persons:[
                    {id:1, name:'san Zhang', age:18},
                    {id:2, name:'si Li', age:19},
                    {id:3, name:'wu Wang', age:20}
                ]
            },
            methods: {
                add() {
                    const p = {id:4, name:'daPao Zhang', age:21};
                    this.persons.push(p);
                }
            },
        });
    </script>
</body>
</html>

解析:为li节点绑定key,key值设置为人物信息中的唯一标识属性id,通过v-for遍历数组对象,将列表渲染到页面中,此外我们定义一个按钮用于操作节点,为列表添加数据。点击按钮,内存中生成新虚拟Dom如下:

新虚拟Dom:

        <li key = 1>san Zhang<li>

        <li key = 2>si Li<li>

        <li key = 3>wu Wang<li>

        <li key = 4>daPao Zhang<li>

旧虚拟Dom:

        <li key = 1>san Zhang<li>

        <li key = 2>si Li<li>

        <li key = 3>wu Wang<li>

        新旧虚拟Dom进行比较,通过以上的比较过程,旧虚拟Dom的key值为1、2、3的li节点依次找到新虚拟Dom的key值为1、2、3的li节点进行比较,比较结果相等,因此在比较过程中,旧虚拟Dom的key值为1、2、3的节点得到了复用,不需要再次渲染,而新创建的key值为4的节点渲染到页面中的真实Dom中,从而大大提高了性能。

QQ录屏20220424173951

二、key的使用

1.经典key错误案例:index作为key

        用index作为key值可能会引发的问题:

1.若对数据进行逆序添加、删除等破坏节点顺序的操作:会产生没必要的Dom更新,效率低

举个栗子:

    <div id="root">
        <!-- 遍历数组 -->
        <h2>The list of persons</h2>
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {{p.id}}--{{p.name}}--{{p.age}}
            </li>
        </ul>
        <button @click="add">Add</button>
    </div>
    <script>
        Vue.config.productionTip = false; 
        const vm = new Vue({
            el:'#root',
            data: {
                persons:[
                    {id:1, name:'san Zhang', age:18},
                    {id:2, name:'si Li', age:19},
                    {id:3, name:'wu Wang', age:20}
                ]
            },
            methods: {
                add() {
                    const p = {id:4, name:'daPao Zhang', age:21};
                    this.persons.unshift(p);
                }
            },
        });
    </script>

        此例我们通过unshift()添加新数据到列表的最前边,同时我们将key值设置为数组的索引值index,生成的新旧虚拟Dom如下:

新虚拟Dom:

        <li key = 0>daPao Zhang<li>

        <li key = 1>san Zhang<li>

        <li key = 2>si Li<li>

        <li key = 3>wu Wang<li>

旧虚拟Dom:

        <li key = 0>san Zhang<li>

        <li key = 1>si Li<li>

        <li key = 2>wu Wang<li>

解析:我们仅仅只是通过添加了一行新的数据,并未对原先三个节点进行修改,因而旧虚拟Dom中本该复用的三个节点在新生成的虚拟Dom中,他们的key值各自向后移动了一位,根据新旧虚拟Dom具有相同key值节点进行比较的规则,旧虚拟Dom的三个节点各自进行没有必要的比较,比较后,旧的三条数据进行了重新渲染,从而产生了没必要的Dom更新。

2.绑定key的Dom结构中包含输入类Dom:会产生错误的Dom更新

举个栗子:

    <div id="root">
        <!-- 遍历数组 -->
        <h2>The list of persons</h2>
        <ul>
            <li v-for="(p,index) in person" :key="index">
                {{p.id}}--{{p.name}}--{{p.age}}
                <input type="text">
            </li>
        </ul>
        <button @click="add">Add</button>
    </div>
    <script>
        Vue.config.productionTip = false; 
        const vm = new Vue({
            el:'#root',
            data: {
                person:[
                    {id:01, name:'san Zhang', age:18},
                    {id:02, name:'si Li', age:19},
                    {id:03, name:'wu Wang', age:20}
                ]
            },
            methods: {
                add() {
                    const p = {id:04, name:'daPao Zhang', age:21};
                    this.person.unshift(p);
                }
            },
        });
    </script>

错误案例2

        此例我们在li节点中添加输入框节点input,并在输入框中输入信息,然后添加人物信息,产生如上错误的Dom更新,这在实际开发中是严重的错误。生成的新旧虚拟Dom如下:

新虚拟Dom:

        <li key = 0>daPao Zhang<input type="text"><li>

        <li key = 1>san Zhang<input type="text"><li>

        <li key = 2>si Li<input type="text"><li>

        <li key = 3>wu Wang<input type="text"><li>

旧虚拟Dom:

        <li key = 0>san Zhang<input type="text"><li>

        <li key = 1>si Li<input type="text"><li>

        <li key = 2>wu Wang<input type="text"><li>

解析:同理,新旧Dom中具有相同key值的两两li节点进行比较,先对li内部的文本节点进行比较从而对更新了对应的真实Dom,而对li节点内部input节点比较时,只对比了该节点结构,而不会对input中的value值进行比较,发现都新旧虚拟Dom中的input节点都相同,因此复用旧虚拟Dom中的input节点,导致产生了错误的Dom更新。

2.如何使用key

        在Vue开发中使用key标识节点时对页面中的数据进行更新时,最好的方式是采用每条数据的唯一标识属性,如id、手机号、身份证号等唯一值。如果不存在对数据进行逆序添加、删除等破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key值是没有问题的。

  • 作者:JV_32
  • 原文链接:https://blog.csdn.net/m0_53375764/article/details/124384866
    更新时间:2023-01-10 14:09:52