动态绑定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>