VUE2 学习笔记5 动态绑定class、条件渲染、列表过滤与排序

发布于:2025-07-25 ⋅ 阅读:(18) ⋅ 点赞:(0)

动态绑定class样式:

先设置css:

    <style>
        .styleBackgroundColor{
            background-color: aqua;
        }
        .styleContent{
            width:300px;
            height: 200px;
        }
        .styleBorder{
            border: 2px black solid;
        }
    </style>

vue模版中,使用动态类名绑定,一般可用以下三种形式

:class="变量名"   

<div id="root">
       <div :class="nowClass">

       </div>
</div>

...

 data() {
         return {
           nowClass:"styleBackgroundColor",
         }
 },

class类名绑定变量名,变量名里存储类名的字符串,一般用于样式类名不确定,需要根据情况改变的情景下。但这种方式只能绑定一个类名。

:class="数组名" (绑定数组,数组里可存放多个类名,可以通过数组动态切换某个类名是否存在,用于要绑定的类名个数不确定,名字也不确定时)

    <div id="root">
       <div :class="nowClass">

       </div>
    </div>

    ...

    data() {
       return {
           nowClass:["styleBackgroundColor","styleContent","styleBorder"],
       }
    },

:class="{ 类名:true/false,类名2:true/false }" (绑定配置对象,对象中key是类名,value是布尔值,表示类是否生效,用于绑定样式个数确定,类名确定,但是要动态确定用不用的情况)但这种格式不便于修改配置内容,也可以把配置对象赋值给一个变量,把变量名绑定给类。

<div :class="{styleBackgroundColor: true,  styleContent:true}">



//或者
    <div id="root">
       <div :class="nowClass">

       </div>
    </div>

    ...

       data() {
                return {
                    nowClass:{
                        styleBackgroundColor:true,
                        styleContent:true,
                        styleBorder:true,
                    },
                }
            },

动态绑定内联样式:

动态绑定内联样式,内联样式要使用配置对象的形式,并且key使用驼峰式命名。

<body>
    <div id="root">
       <div :class="nowClass" :style="nowStyle">
            123
       </div>
    </div>

    ...
            data() {
                return {
                   
                    nowClass:{
                        styleBackgroundColor:true,
                        styleContent:true,
                        styleBorder:true,
                    },
                    nowStyle:{
                        fontSize: '40px',
                    }
                }
            },
        })
    ...

内联样式也可以用数组的形式绑定,数组中每个元素都是配置对象:

<body>
    <div id="root">
       <div  :style="[nowStyle,nowStyle2]">
            123
       </div>
    </div>

    
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        let vm = new Vue({
            el: '#root',
            data() {
                return {
                   
                   
                    nowStyle:{
                        fontSize: '40px',
                    },
                    nowStyle2:{
                        backgroundColor:'blue',
                    }
                }
            },
        })
    </script>
</body>

条件渲染

v-show

v-show用于控制当前标签是否被显示。v-show的value值需要返回布尔值。

v-show为false时,标签只是视觉上看不到了,实际上标签还在。false实际上是给标签添加了display:none。

v-if

v-if的效果和v-show类似,也是控制标签是否被显示。

v-if的value值也需要返回布尔值。

当v-if的值为false时,对应的标签会直接从页面上消失,结构都会被删掉。

v-else/v-else-if

和v-if搭配使用,和if(){...} else if(...){} else的逻辑是一样的。

用v-if v-else-if v-else时,对应的标签需要是连续的,如果中间多了一个不含条件判断的标签,则该标签后面的标签判断不会生效,而且会报错。

v-show与v-if:

如果标签有很高的切换频率,更建议使用v-show,因为v-show只是切换一下display,切换消耗比较低。而v-if在频繁切换时,会不断地从页面上删除一个节点,添加一个节点,删除一个节点,添加一个节点,切换消耗比较高。

小tips:

对于渲染逻辑一样的标签,在不破坏结构的情况下,可以把标签包在同一个div里(但要注意,如果这种行为会破坏html结构,影响css选择器的选择,则不应该这么使用)。

对于渲染逻辑一样的标签,更好的做法,是可以把标签包在一个template标签里。与div不同,template不会破坏html结构。但template只能和v-if一起使用,不能和v-show一起使用。

列表渲染

列表,以<ul> <li>标签构成。

列表一般结合v-for进行渲染,v-for用于循环生成要渲染的标签。这里简单介绍一下v-for。

v-for

语法:v-for="(元素形参,索引形参) in 要遍历的变量" :key=""。

    <div id="root">
        <ul>
            <li v-for="(data,index) in listData" :key="index">
                {{ data.name }}:{{data.age}} -- {{index}}
            </li>
        </ul>
    </div>
            data() {
                return {
                   listData:[
                    {name:'catcat',age:5},
                    {name:'dogdog',age:8},
                    {name:'QAQ',age:15},
                   ]
                }
            },

v-for的形参默认有两个:元素形参,索引形参,可以只使用一个(元素形参),但访问超过两个会返回undifined。

key:

key是每一条标签数据的标识。不写key,其实也不会报错,但尽量还是写key,每条标签的key应该不同。在标签内部,v-for中的数据用{{}}获取。key是Vue内部使用的,最终的渲染标签中是没有key的。

当把key值赋值成Index时,在某些情况下,一般是逆序添加数据、逆序删除数据,这会产生问题,最好不要把key赋值成Index

使用Index作为key,如果有一个数组,现在需要在数组最前面加一条数据,每条数据都有自己的input框,当新加一条数据时,input框中的数据会发生错乱。

<body>
    <div id="root">
        <button @click.once="addData">add</button>
        <ul>
            <li v-for="(data,index) in listData" :key="index">
                {{ data.name }}:{{data.age}} -- {{index}}
                <input type="text">
            </li>
        </ul>
    </div>

    <style>
        .styleBackgroundColor{
            background-color: aqua;
        }
        .styleContent{
            width:300px;
            height: 200px;
        }
        .styleBorder{
            border: 2px black solid;
        }
    </style>

    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        let vm = new Vue({
            el: '#root',
            data() {
                return {
                   listData:[
                    {name:'catcat',age:5,id:'001'},
                    {name:'dogdog',age:8,id:'002'},
                    {name:'QAQ',age:15,id:'003'},
                   ]
                }
            },
            methods:{
                addData(){
                    this.listData.unshift({name:'birdbird',age:2,id:'004'});
                }
            }
        })
    </script>
</body>

点击add按钮前:

点击add按钮后:

会出现这种现象,和diff算法与key有关:

在DOM变化前,Vue会拿初始数据生成虚拟DOM,DOM上有初始的key值,key值以对应的index赋值。然后Vue会将虚拟DOM转换为真实DOM,显示在页面上,用户在页面上操作真实DOM。虚拟DOM存在于内存中,用户是看不到的,用户能够交互到的都是真实DOM。

用户输入的数据会存储在真实DOM上,当点击按钮时,后台出现了新的数据。Vue会根据新的数据生成新的虚拟DOM,新的虚拟DOM中,每条数据的key仍然是对应的index值。但由于元素的顺序发生了变化,前后两次生成的虚拟DOM中,同一条数据的key并不相同。

Vue会执行diff算法,把新的虚拟DOM和旧的虚拟DOM进行比较。diff的比较依据是key,Vue会先比较新虚拟DOM中某条key在旧DOM中有没有,如果有,Vue会比较两个内容,如果两个内容完全一样,会直接复用,如果不一样,Vue会渲染新虚拟DOM中的内容,而不会复用。对于比较的对象,并不是整个数据,而是标签、属性、文本等。对于key=“0”的两条数据<li key="0">catcat:5--1<input type="text"></li>  <li key="0">birdbird2--0<input type="text"></li>。Vue会认为这两条数据的内容字符串“catcat:5--1”和“birdbird2--0”不一样,但是input标签还是一样的,因为input的数据目前只存储在了前台页面上,后台看不到。因此Vue会替换文本内容,但是会复用input框。对于最后一条数据,由于他的key是3,旧虚拟DOM中没有key:'3',Vue会认为这是新的DOM,会新生成input框,因此里面没有数据。最终就会导致页面显示发生错乱。

修改key值,让两次DOM相同数据的key值是一样的,可以修复这个问题。

在不需要Input框的情况下,也许用index作为key不会产生页面上效果的错乱,但可能仍然会导致效率问题,因为Vue比较时,会认为数据都是新的,无法复用旧DOM,因此会产生效率问题。

不写key的情况下:如果不写key,Vue会默认把index作为key,同样会造成问题的产生。

key的作用:key是虚拟DOM的标识,对比新旧DOM时,是比较key相同的DOM,相同的内容直接复用,不同的内容会生成新的DOM。

关键词in也可以换成of。变量中有多少条数据,v-for就会循环多少次。

v-for通常遍历的是数组,但是也可以用来遍历字符串等结构。当遍历字符串时,遍历的是字符串中的每个字符,索引是字符对应的下标。

    <div id="root">
        <ul>
            <li v-for="(data,index) in 'aefaefaefae'" :key="index">
                {{ data }} -- {{index}}
            </li>
        </ul>
    </div>

也可以指定遍历的次数:v-for="(当前遍历次数,索引)in 指定次数"。

在这种形式下,当前遍历次数从1遍历到指定次数,索引从0遍历到指定次数-1。

    <div id="root">
        <ul>
            <li v-for="(data,index) in 6" :key="index">
                {{ data }} -- {{index}}
            </li>
        </ul>
    </div>

列表筛选/列表过滤

从列表中筛选出满足条件的数据。

有一个列表,和一个输入框,当输入框中输入完内容后,重新进行过滤。

如何判断输入的内容改变了:使用watch或者computed。

用watch:监视输入框,输入框改变时,对列表进行过滤,过滤使用Array.filter方法。但要注意,由于filter不更改原数组,因此要记得把返回值赋值给列表。但是不能赋值给原数据,这样会导致每过滤一次,都可能删除原数组中的内容,原数组会越来越少。可以用一个过滤数组保存过滤值,每次在原数组上filter,把返回值返给过滤数组,页面上显示过滤数组。过滤数组默认为原始数组。

当输入框中的内容被输入,但又被删空时,会监听到输入框中的值是空字符串,filter拿空字符去匹配,会返回0,因此此时显示的是整个列表。

当没进行搜索时,应该显示原始数组,这时可以设置immediate:true,让filter拿空字符串去匹配。

用computed:计算返回数组,数组值通过输入框里的内容进行过滤。逻辑和上面watch里面的内容是一样的。由于计算属性在一开始会被调用一次,因此不需要担心过滤数组的初始值问题。

vscode可以用#region #endregion包裹住想折叠的代码,就可以有折叠按钮。

当watch和computed都能实现功能时,一般用computed实现。

代码:

<body>
    <div id="root">
        <input v-model="keyWords">
        <ul>
            <li v-for="(data,index) in filterData" :key="data.id">
                {{ data.name }}:{{data.age}} -- {{index}}
               
            </li>
        </ul>
    </div>

    <style>
        .styleBackgroundColor{
            background-color: aqua;
        }
        .styleContent{
            width:300px;
            height: 200px;
        }
        .styleBorder{
            border: 2px black solid;
        }
    </style>

    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        let vm = new Vue({
            el: '#root',
            data() {
                return {
                   listData:[
                    {name:'catcat',age:5,id:'001'},
                    {name:'dogcat',age:8,id:'002'},
                    {name:'QAQ',age:15,id:'003'},
                    {name:'QAQTAT',age:15,id:'004'},
                    {name:'123',age:15,id:'005'},
                   ],
                   keyWords:'',
                }
            },
            computed:{
                filterData(){
                    return this.listData.filter( (x)=>{
                        return x.name.indexOf(this.keyWords)!==-1;
                    })
                }
            }
        })
    </script>
</body>

排序

升序降序是对过滤数组进行升降序。

排序可以通过重写sort的排序函数实现。

虽然也可以把排序逻辑写在按钮上,但这样每个按钮都需要一个点击函数,比较冗余,可以把排序逻辑也写在computed中。

<body>
    <div id="root">
        <input v-model="keyWords">
        <button @click=" orderData = 'asc' ">按age升序</button>
        <button @click=" orderData = 'desc' ">按age降序</button>
        <button @click=" orderData = 'ori' ">原顺序</button>
        <ul>
            <li v-for="(data,index) in filterData" :key="data.id">
                {{ data.name }}:{{data.age}} -- {{index}}
               
            </li>
        </ul>
    </div>

    <style>
        .styleBackgroundColor{
            background-color: aqua;
        }
        .styleContent{
            width:300px;
            height: 200px;
        }
        .styleBorder{
            border: 2px black solid;
        }
    </style>

    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        let vm = new Vue({
            el: '#root',
            data() {
                return {
                   listData:[
                    {name:'catcat',age:5,id:'001'},
                    {name:'dogcat',age:8,id:'002'},
                    {name:'QAQ',age:15,id:'003'},
                    {name:'QAQTAT',age:15,id:'004'},
                    {name:'123',age:15,id:'005'},
                   ],
                   keyWords:'',
                   orderData:'ori',
                }
            },
            computed:{
                filterData(){
                    const oriData =  this.listData.filter( (x)=>{
                        return x.name.indexOf(this.keyWords)!==-1;
                    });

                    if(this.orderData != 'ori'){
                        if(this.orderData == 'desc'){
                            oriData.sort( function(data1,data2){
                                return data2.age - data1.age;
                            } )
                        }else{
                            oriData.sort( function(data1, data2){
                                return data1.age - data2.age;
                            }

                            )
                        }
                    }
                    
                    return oriData;
                }
            }
        })
    </script>
</body>


网站公告

今日签到

点亮在社区的每一天
去签到