Vue的列表渲染和列表排序学习笔记

180 阅读6分钟

列表渲染

基于人员列表案例遍历数组

v-for基于一个数组来渲染一个列表,使用形参p来遍历persons数组中的数据(像js中的for-in遍历) 可以使用:key动态绑定index索引值

记录本次学习过程中的第一个bug

代码和debug console的截图如下

列表渲染第一次错误记录.png

列表渲染第一次错误1.png

消除bug

 <li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>

在persons中接收多个元素 (a,b)in persons,也可以用of代替in

基于汽车案例遍历对象

html

 <h2>做梦素材</h2>
        <ul>
            <li v-for="(value,key) in car" :key="key">
                {{key}}-{{value}}
            </li>
        </ul>

script

car: {
         name: '玛莎拉蒂',
         price: '200w',
         color: 'gold'
    }

运行结果

做梦素材运行截图.png

遍历字符串

在data数据中添加字符串str:'hello',按照之前遍历对象的方式来遍历字符串 得到的运行结果截图如下

遍历字符串运行结果.png

<!-- 遍历字符串 -->
        <h2>测试遍历字符串</h2>
        <ul>
            <li v-for="(char,index) of str" :key="index">
                {{key}}-{{value}}
            </li>
        </ul>

将index用作key不太合适,因为……(不涉及更新列表此时忽略不合适) 此处无太大功能暂时这样了

遍历指定次数(不常见)

遍历指定次数不需要对象数组等的数据,可以直接遍历一个数字

<li v-for="(a,b) of 5">
  {{a}}-{{b}}
</li>

运行结果截图

遍历指定次数截图.png 所以a是数字值,b是索引值。

总结

v-for指令

  1. 用于展示列表数据
  2. 语法: v-for="(item,index) in xxx" :key="yyy"
  3. 可遍历:数组,对象,字符串(用的很少)、指定次数(用的很少)

key的原理

key可以对节点进行标识,相当于人类的身份证号。 在上述基于人员列表遍历数组的代码案例中,如果没有key列表的展示不会受到影响

需求:添加一个人在persons的数据的最前面,(真实项目开发中id不是前端人员进行维护的,数据传给服务器,服务器传给数据库,数据库处理数据生成id)案例中简单处理,按照自增长规律实现自增长。

在人员列表每个人员后面加入输入框,input写在li里面,在input输入框里面添加文字,点击一次性按钮添加人员。

实操过程第一次小白bug记录

添加小狗功能报错.png 最后找到原因是因为 列表渲染添加小狗失败原因.png methods应该和data并列而不是和data的内容并列。(透露我是一个小小白)

言归正传,代码正常运行后的截图如下

列表渲染input框内容错位.png input输入框的内容因为新添加的人员而错位,原因是index作为key,过程如图,

渲染列表key的作用.jpg

一样的复用,不一样的生成新的DOM,随后替换页面中之前的真实DOM 如果对顺序做了破坏性操作,index做key会出问题。id作为key就不会有问题,因为id具有唯一性。

面试题

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 ,会产生错误的DOM更新,界面会有问题
  4. 开发中如何选择key?
    1. 最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号,学号等唯一值
    2. 如果不存在破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

列表过滤

通过watch来实现监视变化,当用户输入的keyword发生变化时对数据进行过滤,监视keyWord,因为输入新的值不用关注旧的值所以不用传旧的值的参数。

 <div id="filtrate">
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <ul>
            <li v-for="(p,index) of  persons" :key="index">
                {{p.name}}--{{p.age}}--{{p.sex}}
            </li>
        </ul>
    </div>
    <script>
        const vm = new Vue({
            el: '#filtrate',
            data: {
                keyWord: '',
                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: '男' }
                ],

            },
            watch: {
                keyWord: (val) =>
                    console.log('keyword被改了', val)
            }
        })
    </script>

在输入框输入文字后

一开始keyWord写的普通函数报错了,(我对普通函数有什么误解) watch里的内容(正确的)

     keyWord(val) {
         console.log('keyword被改了', val)
     }

报错是因为我在keyWord和参数之间写了冒号,就想着这普通函数为啥有毛病了???(实际这真的是普通函数吗哈哈哈) 不过还是建议写成箭头函数

    watch: {
        keyWord: (val) =>
            console.log('keyword被改了', val)
            }

判断字符里是否包含指定字符使用indexOf,如果不包含返回-1,如果包含返回字符所在字符串的所在位置的索引值

    keyWord(val) {
        this.persons = this.persons.filter((p) => {
            return p.name.indexOf(val) !== -1
        })
    }

上述代码可以实现,在输入框输入一个列表中人员的姓或者名,可以显示包含该姓名的人员,但是存在问题 第一次输入冬,显示周冬雨和马冬梅,列表过滤后更新,原来的其余的两个人的数据就丢失了,此时如果搜索周不会显示任何人的信息,如果想保留原数据,可以新建一个数据集合在data里面用来保存过滤后的数据,这样就可以保留原数据在原来的persons数据里面,同时修改上面的遍历的代码"(p,index) of persons"改为"(p,index) of filPersons",但是这样最初打开页面由于filPersons数据是空的所以啥都没有,可以在啥不知道的情况下进行查询然后就会在filPersons保存数据 当输入空字符串的时候会返回所有数据,因为indexOf''会返回所有的数据 如果想要在开始的时候显示所有数据,完整代码如下

keyWord: {
     immediate: true,
     handler(val) {
         this.filPersons = this.persons.filter((p) => {
             return p.name.indexOf(val) !== -1
         })
     }
 }

上述代码可以在页面打开的时候调用一次,所以最初的页面会显示所有的数据,下面用computed实现

computed: {
     filPersons() {
         return this.persons.filter((p) => {
             return p.name.indexOf(this.keyWord) !== -1
         })
     }
 }

列表排序

sortType 0原顺序 1降序,2升序 判断是都可以排序 if(this.sortType !==0)也可以写 if(this.sortType),无论是1还是2转成布尔值都是真

先过滤后排序,排序降序是后-前,p1,p2是对象,排序应该是对象的属性,因为改变的是过滤后的数据,所以点击原顺序可以返回最初的不变的数据集

computed: {
                filPersons() {
                    const arr = this.persons.filter((p) => {
                        return p.name.indexOf(this.keyWord) !== -1
                    })
                    // 判断一下是否需要排序
                    if (this.sortType) {
                        arr.sort((p1, p2) => {
                            //函数体
                            return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
                        })
                    }
                    return arr
                }
            }