key

这个在列表渲染中,极为重要,通常把数据中的id当做这个key值,这个id通常是由后端传过来的唯一标识符,例如uuid。
直接上代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>江河三千里</title>
    <script type="text/javascript" src="http://ljy0427.online/js/vue.js"></script>
</head>

<body>

    <div id="app">
        <div v-for="(item,index) in users" :key="index">
            <div>
                {{item.name}}--{{item.age}}---{{index}}
                <input type="text">
            </div>
        </div>
        <button @click="addUser">添加用户</button>
    </div>

</body>

<script type="text/javascript">
    Vue.config.productionTip = false
    var vm = new Vue({
        el: '#app',
        data: {
            users:
                [
                    { id:001,name: "江河", age: "18" },
                    { id:002,name: "云月", age: "20" },
                    { id:003,name: "三千", age: "21" },
                    { id:004,name: "九千", age: "24" }
                ]
        },
        methods: {
           addUser(){
               var id=Math.floor(Math.random()*5)
               const tr={ id:00+id,name: "TR"+id, age: "18" -id }
               this.users.unshift(tr)
           }
        }
    });
</script>

</html>

拟定这样的需求,就每次都给这个users添加用户,并且添加在第一个,首先将key值设置为index值
请输入图片描述

通过以上操作可以看错很大的问题,怎么我使用头插法添加元素,我之前所在input框中输入的东西,不对应了,乱了,而且乱得还很有规律,都随着元素的增加,input框中的值,都往前移了。

这个就是由于使用了index当做了key,做造成的。

原理

首先分清什么是虚拟dom和真实dom
虚拟dom是Vue所管理的东西,也是看不见的;而真实dom就是用户所看见的,所使用的元素。也就是当页面中的元素经过用户的操作所改变,这里实际操作的是真实dom,然后Vue捕获对应的事件,修改其中的虚拟dom,并且重新渲染到页面上。

案例分析

在这个案例中,当用户想input框中写入数据时,就是操作了真实dom,并没有修改其对应的虚拟dom,也就是内存地址。
当对应的数据有发生改变时,会根据新的数据生成虚拟dom,并且在这个过程中会产生很关键的一步,虚拟dom的对比,也就是改变前后两个虚拟dom的对比算法,而这个对比算法的依据就是key值,并且在对比过程中,如果前后的值不相同,后者的值将会覆盖前者的
即将key值相等的元素进行比较。

解释

例如这里的第一个数据,以index作作为key,也就是江河这个用户的key为0,并且还对同一层级的input框进行了数据的修改。
后面通过点击事件,在users的一开始添加了用户TR,这种时侯,这个TR的key值就为0了,江河的key值就为1了。
说到这里,根据前面说的对比值,如果不相同,那么后者将覆盖前者啊。然后这个案例中,添加了TR用户之后,input框中的值啥也没有,这种时候,前后input对比的时候,都不相同了,不应该是后者,然后依次类推,应该是都没啊,就像如下。

的确,这样确实没毛病,但却忽视了一个很重要的问题,虚拟dom
用户在对input框进行修改的时候,修改的是真实dom,并不是虚拟dom,也就是并没有修改到当前虚拟dom的内存地址,所以,在对比的时候,发现地址不变,就跳过了,里面的东西变没变,就不管了。因此就出现了添加了TR用户之后,其input框中的值是之前江河用户那一栏的数据了。

更换key

虽然在上述过程中,设置了key为index,但其实这样,就相当于没有设置。如果要设置,就需要将key值设为id这唯一标识符。
<body>
    <div id="app">
        <div v-for="(item,index) in users" :key="item.id">
            <div>
                {{item.name}}--{{item.age}}---{{item.id}}
                <input type="text">
            </div>
        </div>
        <button @click="addUser">添加用户</button>
    </div>
</body>
<script type="text/javascript">
    Vue.config.productionTip = false
    var vm = new Vue({
        el: '#app',
        data: {
            users:
                [
                    { id:001,name: "江河", age: "18" },
                    { id:002,name: "云月", age: "20" },
                    { id:003,name: "三千", age: "21" },
                    { id:004,name: "九千", age: "24" }
                ],
                idArr:[1,2,3,4]
        },
        methods: {
           addUser(){
               var id=Math.floor(Math.random()*100)
               var findIndex=this.idArr.indexOf(id);
               if(findIndex==-1){
                this.idArr.push(id);
                const tr={ id:00+id,name: "TR"+id, age: "18" -id }
                this.users.unshift(tr)
               }else{
                   alert("当前用户id已存在,重新添加")
               }
           }
        }
    });
</script>
这种时候,通过用户的唯一标识来作为key,就解决了这个问题。

总结

在写这种v-for列表渲染的时候,尽量将key设置为后端传过来的唯一标识符。
最后修改:2022 年 05 月 07 日