動態綁定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>