12Vue列表渲染

90 阅读3分钟

1.v-for 展示列表数据

1.1v-for的用法

v-for指令:

1.用于展示列表数据

2.语法:v-for="(item, index) in xxx" :key="yyy" 也可以用 v-for="item in XXX",使用in也是可以的

3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

         <!-- 遍历数组的写法(用得最多) -->
        <h2>人员列表</h2>
        <ul>
​
            <li v-for="p in persons" :key="p.id">
                {{p.id}}
            </li>
​
            <li v-for="(p,index) in persons" :key="index">
                {{p.id}}
            </li>
        </ul>
​
​
​
        <!-- 遍历对象的写法 -->
        <h2>汽车信息</h2>
​
        <li v-for="(value,key) in car" :key="key">
            {{value}}--{{key}}
        </li>
​
​
​
        <!-- 遍历字符串的写法( 用得比较少) -->
        <h2>测试遍历字符串</h2>
        <ul>
            <li v-for="(a,b) in str">
                <!-- a就是每个字符,b就是每个字符的索引值 -->
                {{a}}-{{b}}
            </li>
        </ul>
​
​
​
        <!-- 遍历指定次数(非常少见) -->
        <h2>测试遍历字符串</h2>
        <ul>
            <li v-for="(a,b) in 5">
                <!-- a从1开始往后计数,1、2、3这样,b就是每个数字的索引值 -->
                {{a}}-{{b}}
            </li>
        </ul>
​
    </div>
    <script>
        new Vue({
            el: '#vu',
            data: {
                persons: [
                    { id: '001', name: '张三', age: '18' },
                    { id: '002', name: '李四', age: '19' },
                    { id: '003', name: '王五', age: '20' }
                ],
                car: {
                    name: '奥迪A8',
                    price: '50万',
                    color: '黑色'
                },
                str: 'hello'
​
            }
        })
    </script>

1.2 v-for中key的原理

先看一个小案例,以index为索引在现有的列表数据前面添加一条数据,会出现输入框与数据的错配情况

1657328647661.png

1657328717431.png

1657328731346.png

产生此问题的原因如下:

1657328954946.png

原始数据→Vue生成虚拟dom→虚拟dom转为真实dom,此时在输入框输入数据(数据残留在真实dom中)

插入新的数据→Vue根据新数据产生新的虚拟dom→新旧虚拟dom进行对比,key相同的在一起对比,在Vue看来,新、旧虚拟dom中如果有相同的部分,则新dom无需再转化为真实dom,直接把旧虚拟dom已经转好的真实dom拿过来即可,而新旧虚拟dom中的 input标签在Vue看来是一样的,直接把旧dom转化好的真实dom拿过来用即可。问题的关键就在于,新虚拟dom中插入的数据占据了key=0这个位置(因为采用的是索引值作为key),这就导致了新旧虚拟dom是不同的(key值相同的情况下)。

解决这个问题,可以在v-for的时候采用数据自身的标识(如id)来作为key。

 <div id="vu">
        <!-- 遍历数组的写法(用得最多) -->
        <h2>人员列表</h2>
​
        <button @click.once="add">添加一个老刘</button>
        <ul>
            <!-- p是person中每项的值, index是索引 -->
            <li v-for="(p,index) in persons" :key="p.id">
                {{p.name}} {{p.age}}--{{index}}
                <input type="text">
            </li>
​
        </ul>
​
    </div>
    <script>
        new Vue({
            el: '#vu',
            data: {
                persons: [
                    { id: '001', name: '张三', age: '18' },
                    { id: '002', name: '李四', age: '19' },
                    { id: '003', name: '王五', age: '20' }
                ],
​
            },
            methods: {
                add() {
                    //定义老刘的个人信息,将其添加在persons 数组的前面
                    const p = { id: '004', name: '老刘', age: '60' };
​
                    this.persons.unshift(p);
                }
            },
        })
    </script>

1657329900916.png

1.3key原理的总结

面试题:react、vue中的key有什么作用?(key的内部原理)

1.虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,

随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:

(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。

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

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
        2. 如果结构中还包含输入类的DOM:
        3. 会产生错误DOM更新 ==> 界面有问题。
  1. 开发中如何选择key?:

1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

2.列表过滤

2.1搜索小案例

在输入框输入名字(单个字或者全名),列表展示筛选过的数据。

2.1.1watch方法实现

 <div id="vu">
        <!-- 遍历数组的写法(用得最多) -->
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="input">
        <ul>
​
            <li v-for="p in filePersons" :key="p.id">
                {{p.id}}{{p.name}}
            </li>
            <br>
            {{input}}
​
​
        </ul>
​
​
​
​
    </div>
    <script>
        new Vue({
            el: '#vu',
            data: {
                persons: [
                    { id: '001', name: '马冬梅', age: '18', sex: '女' },
                    { id: '002', name: '周冬雨', age: '19', sex: '女' },
                    { id: '003', name: '周杰伦', age: '20', sex: '男' },
                    { id: '004', name: '温兆伦', age: '20', sex: '男' }
                ],
                input: '',
                filePersons: []
            },
            watch: {
                input: {
                    immediate: true,
                    handler(val) {
                        this.filePersons = this.persons.filter((p) => {
                            return p.name.indexOf(val) != -1;
                        })
                    }
                }
            }
​
​
        })
    </script>

2.1.2computed实现

 <div id="vu">
        <!-- 遍历数组的写法(用得最多) -->
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="input">
        <ul>
​
            <li v-for="p in filePersons" :key="p.id">
                {{p.id}}{{p.name}}
            </li>
            <br>
            {{input}}
​
​
        </ul>
​
​
​
​
    </div>
    <script>
        new Vue({
            el: '#vu',
            data: {
                persons: [
                    { id: '001', name: '马冬梅', age: '18', sex: '女' },
                    { id: '002', name: '周冬雨', age: '19', sex: '女' },
                    { id: '003', name: '周杰伦', age: '20', sex: '男' },
                    { id: '004', name: '温兆伦', age: '20', sex: '男' }
                ],
                input: '',
​
            },
            computed: {
                filePersons() {
                    return this.persons.filter((p) => {
                        return p.name.indexOf(this.input) != -1;
                    })
​
​
                }
            }
​
​
​
        })
    </script>

2.1.3 列表排序+过滤

 <div id="vu">
        <!-- 遍历数组的写法(用得最多) -->
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="input">
        <button @click="sortType = 1">年龄升序</button>
        <button @click="sortType = 2">年龄降序</button>
        <button @click="sortType = 0">原始排序</button>
        <ul>
​
            <li v-for="p in filePersons" :key="p.id">
                {{p.id}}{{p.name}}-{{p.age}}-{{p.sex}}
            </li>
            <br>
            {{input}}
​
​
        </ul>
​
​
​
​
    </div>
    <script>
        new Vue({
            el: '#vu',
            data: {
                persons: [
                    { id: '001', name: '马冬梅', age: '30', sex: '女' },
                    { id: '002', name: '周冬雨', age: '19', sex: '女' },
                    { id: '003', name: '周杰伦', age: '36', sex: '男' },
                    { id: '004', name: '温兆伦', age: '20', sex: '男' }
                ],
                input: '',
                sortType: ''
​
            },
            computed: {
                filePersons() {
                    const arr = this.persons.filter((p) => {
                        return p.name.indexOf(this.input) != -1;
                    })
​
                    if (this.sortType) {
                        arr.sort((a, b) => {
                            return this.sortType === 1 ? a.age - b.age : b.age - a.age;
                        })
                    }
​
                    return arr;
​
​
                }
            }
​
​
​
        })
</script>

\