vue:条件渲染,列表渲染,key的作用,虚拟dom与diff算法,列表过滤(查询),列表排序

肝vue的周末,下一次就要直接开始把后面的基础命令过一遍,写写案例就开始做vue工程了,进度还行,争取一周内把底子打扎实吧!还好我之前做的后端学起来比较快。至于文章为什么一直都是随笔的样子?因为我本来就是个不拘无束的性格,写文章怎么舒服怎么来,不必去为了流量和推广。


条件渲染

vue实例中也是有条件渲染的,和我们的原生js不同,vue可以直接使用v-if或者v-show来决定是否渲染这个标签(前者决定dom节点是否生成,后者决定display属性是否为none),前者为false之后无法获取该dom结点进行操作。

一个简单的案例说明二者的区别以及注意事项:

<body>
    <div id="app">
        <!-- v-if达成的条件渲染会直接决定所解析标签的dom结点是否存在! -->
        <h1 v-if="status">我说的是真的</h1>
        <!-- 注意在解析的时候v-else-if的标签一定要跟在v-if的标签的下方,
            不要在中间穿插其它的标签(任何非v-else-if解析的标签),否则会报错! -->
        <h1 v-else-if="!status">我说的不是假的</h1>
        <!-- v-show达成的 条件渲染只是决定了dom结点的style里的display属性是否为none -->
        <h2 v-show="status">你应该信任我!</h2>
        <h2 v-show="!status">你竟然不相信我?</h2>
        <button @click="status = !status">是真是假?</button>
        <!-- template模板只能与v-if一起使用,渲染时只展示标签内容不显示template标签(但是使用模板不影响结构) -->
        <template v-if="status">
            <h2>他觉得他说的是真的</h2>
            <h2>还有一些漏洞没有填坑</h2>
        </template>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    status: true
                }
            }
        })
    </script>
</body>

注释中写到:v-if达成的条件渲染会直接决定所解析标签的dom结点是否存在!注意在解析的时候v-else-if的标签一定要跟在v-if的标签的下方,不要在中间穿插其它的标签(任何非v-else-if解析的标签),否则会报错!v-show达成的 条件渲染只是决定了dom结点的style里的display属性是否为none。

大家来看看v-show渲染的标签属性:

 从这里不难看到当我们的v-show="false"时,这个标签的style中的display值为none。当我们的v-if="fasle"又是什么样子呢?

 

 不难看出直接就没有这几个dom节点了。这就是二者的区别,开发中由于v-if可以与v-else-if以及v-else组成条件代码块,所以比较常用,而且不会造成dom的浪费。


列表渲染

列表渲染在原生的JS里我们是采用for each循环渲染实现的,我们vue提供了v-for来实现数组对象以及字符串的遍历:v-for=" item in(of)  items" v-bind:key="index(或是自定义的标识)" 。这里的key极其重要,之后会说到,这里我们不写key也会默认是index作为关键标识使用在虚拟dom上。我们先来看这个语法的具体使用场景与案例:

场景1:遍历数组

<div id="app">
        <ul>
            <h2>遍历数组</h2>
            <!-- 遍历数组,person是一个对象,且v-bind:绑定key值,后续数据的改变都可以将key值传递给后端 -->
            <li v-for="person in persons" :key="person.id">
                {{person.name}}-{{person.age}}
            </li>
        </ul>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    persons: [
                        { id: 3, name: 'zhangsan', age: 20 },
                        { id: 1, name: 'lisi', age: 21 },
                        { id: 2, name: 'wangwu', age: 21 }
                    ]
                }
            }
        })
    </script>

上面的案例不难看出我们可以遍历一个persons数组来渲染在列表之上。其实这里有一个细节,在遍历的时候我们的传递给li子项的参数可以有两个:

 <!-- 这里传递的参数可以为两个,数组类型遍历时默认先传递value,再传递index索引值,索引可以当做key使用 -->
            <li v-for="(person,index) in persons">
                {{person}} ---- {{index}}
            </li>

我们可以看到传递的有数组中的索引,其实我们遍历一个字符串时也是如此,因为字符串本身不就是一个字符组成的数组吗?

场景2:遍历对象

对于一个对象类型的数据我们可对其的key与value进行遍历:

<h2>遍历对象</h2>
            <!-- 这里是遍历对象类型的数据,我们可以发现默认传递的是value值,后续的是key -->
            <li v-for="(value,key) in myInfo" :key="key">
                {{key}} ---- {{value}}
            </li>

当然在vue实例中data区域我们需要添加这个myInfo对象:

                    myInfo: {
                        name: 'hlc',
                        age: 20,
                        address: '河南省信阳市'
                    },

这三个遍历结果如:

 

那么遍历字符串就不详细描述了,因为字符串的底层不就是个char数组嘛!我们上面对key的重要性做了很多铺垫,那么key到底有什么作用?它为什么如此重要?


 key的作用

我们先看一个问题描述:这里呢我依旧和上面遍历数组时采取一样的代码,key我设置为index我们在li子项内部添加一个input框,里头不加任何多余的属性:

<body>
    <div id="app">
        <ul>
            <h2>遍历数组验证key细节</h2>
            <!-- 先使用index作为key来测试问题,这里需要接收一下index不然是默认没有的 -->
            <li v-for="(person,index) in persons" :key="index">
                {{person.name}}-{{person.age}}--{{index}} <input type="text">
            </li>
            <button @click.once="addNewPerson">添加一个新人</button>
        </ul>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    persons: [
                        { id: 1, name: 'zhangsan', age: 20 },
                        { id: 2, name: 'lisi', age: 21 },
                        { id: 3, name: 'wangwu', age: 21 }
                    ]
                }
            },
            methods: {
                addNewPerson() {
                    const person = { id: 4, name: 'new person', age: 22 }
                    // 我们既然是用索引作为key,那么我们就将新人加在索引为0的地方,
                    // 它会导致之前的全部索引自增1,那么也许会出现什么有意思的现象呢?
                    this.persons.unshift(person)
                }
            },
        })
    </script>
</body>

这里为了更好的体现问题,我写了一个在persons数组中添加索引为0的新对象的方法,为了更好的

体现问题,我先给大家看看页面:

 我们可以看到没有任何问题!这里我先在后面的input框中输入各自的姓名(也没有任何问题):

 

但是!!!当我点击添加新人时(input框串了?名字和前面的对不上了???): 

 

 这里就需要用我们vue的data数据更新时引发的虚拟dom的对比复用算法(也就是diff算法)来解释这一现象了。我们先不急解释这个算法,我们来测试将key改为每个对象的内在属性id,看看还会不会出现这个问题?

  <!-- 这里我们采用id作为key再看看有没有这个问题,仍然是将新人加在0索引位 -->
            <!-- 这里我们发现,就不产生数据错乱的问题了,因为这个input也受到key也就是id的约束,只会跟着id移动 -->
            <li v-for="person in persons" :key="person.id">
                {{person.name}}-{{person.age}} <input type="text">
            </li>
            <button @click.once="addNewPerson">添加一个新人</button>

我们可以看一下页面上的结果(并没有发生所谓的错乱):

 所以在这里我大胆的总结,key最好作为对象内部的属性使用,不要用外部的数组下标索引使用!


虚拟dom与diff算法

其实我们之前的JS将对象渲染到页面上去时,是直接将数据给真实dom的,那么每一次渲染时都会对真实dom进行操作,大大的增加了性能的损耗,vue为了避免每次刷新时重新渲染数据,它建立了一块缓存中的虚拟dom,这块虚拟dom就是第一次渲染时存储data数据的地方,如果第二次渲染较于上次虚拟dom中的数据并没有发生变化则不改变真实dom,当然如果对比之下有些数据进行了改变,vue就会将最新的数据渲染到真实dom上去,对于没改变的数据就不再操作真实dom。那么我们提出一个疑问?数组类型中每一条对象数据都是独立的个体,在虚拟dom中如果不建立标识一一比较的话,对于比较的难度也会增大,假设目前数组为[对象1,对象2,对象3]与虚拟dom中未更改之前的[对象2,对象1,对象3]进行比对,试问?虚拟dom比对后会去更新真实dom吗?

答案是肯定会的,因为你是以数组的索引为key的diff算法就是以你key作为比对的标识来进行比对赋值的,那么为什么上面key那一节描述的案例会发生错乱,原因就是当你以index作为key的时候虚拟dom中你的name与age都是不一样的,但是你的input框却是一样的,所以不同的更改,相同的不变。当你以id作为key时,它会默认的去比对id相同的li,所以就不会出现错乱的现象了。

上图为我们描述了这样一个比对与赋值的过程。


列表过滤(查询)

列表过滤显然就是我们后端经常做的根据关键字进行查询的操作,在vue中我们要想实现这个操作,首先想到的是函数,其次是计算属性,在之后就是监视。为什么呢?因为你查询是需要一个关键字的,首先这个关键字需要我们能接收,就会用到数据的绑定 v-model ,下面我们给出三种查询的方法:

先给出案例:依旧是三个人,我们需要给出以姓名关键字查询的三个方法。

<body>
    <div id="app">
        <ul>
            <h2>遍历数组(过滤)</h2>
            <input type="text" @keyup.enter="secPerson" placeholder="输入所搜关键词" v-model="Key"> 
            <li v-for="person in Persons" :key="person.id">
                {{person.name}}-{{person.age}}
            </li> 
        </ul>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    Key: '',
                    persons: [
                        { id: 1, name: '黄景瑜', age: 20, sex: '男' },
                        { id: 2, name: '景天', age: 21, sex: '男' },
                        { id: 3, name: '王天一', age: 21, sex: '男' }
                    ],
                 }
            },
            methods: {
                secPerson() {
                    this.persons = this.persons.filter((person) => {
                        return person.name.indexOf(this.key) !== -1
                    })
                }
            }
        })
    </script>
</body>

这里我们采用函数的方法进行查询,但是这样我们会修改源数据,这个是不可逆的一个操作,我们可以采用添加一个target数组接收搜索后的结果进行渲染。接下来我们看看计算属性怎么解决这个问题:

                 以下用于计算属性 
             <input type="text" placeholder="输入所搜关键词" v-model="Key">
             <li v-for="person in target" :key="person.id">
                {{person.name}}-{{person.age}}
             </li>

         computed: {
                target: {
                    // 计算属性的值是其get()函数的返回值
                    get() {
                        return this.persons.filter((person) => {
                            return person.name.indexOf(this.Key) !== -1
                        })
                    }
                }
            },

计算属性所依赖的属性发生变化,计算属性就会执行get()方法并将返回值给到他自己,对比监视属性而言计算属性节约内存!让我们来看看监视属性的代码实现:

            <input type="text" placeholder="输入所搜关键词" v-model="Key">
 <!-- 用于监视过滤 -->
            <li v-for="person in filPersons" :key="person.id">
                {{person.name}}-{{person.age}}
            </li>

        new Vue({
            el: '#app',
            data() {
                return {
                    Key: '',
                    persons: [
                        { id: 1, name: '黄景瑜', age: 20, sex: '男' },
                        { id: 2, name: '景天', age: 21, sex: '男' },
                        { id: 3, name: '王天一', age: 21, sex: '男' }
                    ],
                    // 为解决监视操作源数据的问题创建新数组
                    filPersons: []
                }
            },
           watch: {
                Key: {
                    // 开启初始化触发handler函数会解决初次显示空的问题
                    immediate: true,
                    handler(newVal) {
                        console.log('Key变化' + newVal)
                        // filter不操作原数组它会创建新数组
                        this.filPersons = this.persons.filter((person) => {
                            return person.name.indexOf(newVal) !== -1
                        })
                    }
                }

            }
       })

不难发现用监视属性我们需要建立一个数组接收监视搜索到的新数组内容,否则就会更改源数据,造成不可逆的显示。


列表排序

对于列表排序而言,无非就是三种顺序,降序,升序和原顺序,当然对于我们后端程序员而言,数据的排序我们会分页好传给前端的,既然学vue有这个内容,我们就写一下排序也无妨。先了解一个函数数组的sort()函数的作用与用法:

 let arr = [1,4,3,2,6,8,7,9,5]
        arr.sort((a,b)=>{
            return a-b 
        })

 以上这种写法就是升序的写法,return b-a就是降序的写法,而sort函数会直接修改原数组。

我们来看案例:

<body>
    <div id="app">
        <ul>
            <h2>遍历数组(排序)</h2>
            <input type="text" placeholder="输入所搜关键词" v-model="Key">
            <button @click="sort = 2">根据年纪升序</button>
            <button @click="sort = 1">根据年纪降序</button>
            <button @click="sort = 0">原顺序</button>
            <li v-for="person in target" :key="person.id">
                {{person.name}}-{{person.age}}
            </li>
        </ul>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data() {
                return {
                    // 定义sort标识排序类型 0表示原顺序,1表示降序,2表示升序
                    sort: 0,
                    // 定义Key存储搜索关键字    
                    Key: '',
                    persons: [
                        { id: 1, name: '黄景瑜', age: 20, sex: '男' },
                        { id: 2, name: '景天', age: 21, sex: '男' },
                        { id: 3, name: '王天一', age: 19, sex: '男' }
                    ]
                }
            },
            // 计算属性的触发主要由所依赖的属性变化与否决定
            computed: {
                target: {
                    get() {
                        const arr = this.persons.filter((person) => {
                            return person.name.indexOf(this.Key) !== -1
                        })
                        if (this.sort) {
                            // 这里需要描述一下数组的sort函数,它会直接改变原数组
                            // 它内部需要传递前项和后项两个参数,返回的是前-后就是升序,后-前就是降序
                            return arr.sort((p1, p2) => {
                                return this.sort == 1 ? p2.age - p1.age : p1.age - p2.age
                            })
                        }
                        return arr
                    }
                }
            }
        })
    </script>
</body>

降序的结果就是:

 搜索之后点击升序:

 那么今天的总结到此为止,下午我就要享受周末时光了  ~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ForestSpringH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值