Vue.js 学习笔记 第5章 内置指令
?
本篇目錄:
5.1 基本指令
5.2 條件渲染指令
5.3 列表渲染指令 v-for
5.4 方法與事件
5.5 實戰(zhàn):利用計算屬性、指令等知識開發(fā)購物車
?
回顧一下第2.2節(jié),我們己經(jīng)介紹過指令(Directive)的概念了,Vue.js的指令是帶有特殊前綴v-的HTML特性,它綁定一個表達式,并將一些特性應(yīng)用到DOM上。
其實我們已經(jīng)用到過很多Vue內(nèi)置的指令,比如v-html、v-pre,還有上一章的v-bind。
本章將繼續(xù)介紹Vue.js中更多常用的內(nèi)置指令。
5.1 基本指令
5.1.1 v-cloak
v-cloak不需要表達式,它會在Vue實例結(jié)束編譯時從綁定的HTML元素上移除,經(jīng)常和CSS的display:none;配合使用:
1 <div id="app" v-cloak> 2 {{message}} 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 message: "這是一段文本" 10 } 11 }); 12 </script>?
這時雖然已經(jīng)加了指令v-cloak,但其實并沒有起到任何作用。
當(dāng)網(wǎng)速較慢、Vue.js文件還沒加載完時,在頁面上會顯示{{message}}的字樣。
直到Vue創(chuàng)建實例、編譯模板時,DOM才會被替換,所以這個過程屏幕是有閃動的。
只要加一句CSS就可以解決這個問題了:
?
在一般情況下,v-cloak是一個解決初始化慢導(dǎo)致頁面閃動的最佳實踐,對于簡單的項目很實用,但是在具有工程化的項目里,比如后面進階篇將介紹webpack和vue-router時,項目的HTML結(jié)構(gòu)只有一個空的div元素,剩余的內(nèi)容都是由路由去掛載不同組件完成的,所以不再需要v-cloak。
5.1.2 v-once
v-once也是一個不需要表達式的指令,作用是定義它的元素或組件只渲染一次,包括元素或組件的所有子節(jié)點。
首次渲染后,不再隨數(shù)據(jù)的變化重新渲染,將被視為靜態(tài)內(nèi)容,例如:
?
v-once在業(yè)務(wù)中也很少使用,當(dāng)你需要進一步優(yōu)化性能時,可能會用到。
5.2 條件渲染指令
5.2.1 v-if、v-else-of、v-else
與JavaScript的條件語句if、else、else if類似,Vue.js的條件指令可以根據(jù)表達式的值在DOM中渲染或銷毀元素/組件。
例如:
?
v-else-if要緊跟v-if,v-else要緊跟v-else-if或v-if。
表達式的值為true時,當(dāng)前元素/組件及所有子節(jié)點將被渲染,為false時被移除。
如果一次判斷的是多個元素,可以在Vue.js內(nèi)置的<template>元素上使用條件指令,最終渲染的結(jié)果不包含鈣元素。
例如:
?
Vue在渲染元素時,出于效率考慮,會盡可能地復(fù)用已有的元素而非重新渲染。
比如下面的示例:
?
如圖5-1和圖5-2所示,鍵入內(nèi)容后,點擊切換按鈕,雖然DOM變了,但是之前在輸入框鍵入的內(nèi)容并沒有改變,只是替換了placeholder的內(nèi)容,說明<input>元素被復(fù)用了。


如果你不希望這樣做,可以使用Vue.js提供的key屬性,它可以讓你自己決定是否要復(fù)用元素,key的值必須是唯一的。
例如:
?
給兩個<input>元素都增加key后,就不會復(fù)用了,切換類型時鍵入的內(nèi)容也會被刪除,不過<label>元素仍然是被復(fù)用的,因為沒有添加key屬性。
5.2.2 v-show
v-show的用法與v-if基本一致,只不過v-show是改變元素的CSS屬性display。
當(dāng)v-show表達式的值為false時,元素會隱藏,查看DOM結(jié)構(gòu)會看到元素上加載了內(nèi)聯(lián)樣式display:none。
例如:
?
渲染后的結(jié)果為:
1 <div id="app"> 2 <p style="display: none;">當(dāng)status為1時顯示此行</p> 3 </div>?
提示:
v-show不能在<template>上使用。
5.2.3 v-if與v-show的選擇
v-if和v-show具有類似的功能,不過v-if才是真正的條件渲染,它會根據(jù)表達式適當(dāng)?shù)劁N毀或重建元素及綁定的事件或子組件。
若表達式初始值為false,則一開始元素/組件并不會渲染,只有當(dāng)條件第一次變?yōu)閠rue時才開始編譯。
而v-show只是簡單的CSS屬性切換,無論條件真與否,都會被編譯。
相比之下,v-if更適合條件不經(jīng)常改變的場景,因為它切換開銷相對較大,而v-show適用于頻繁切換條件。
5.3 列表渲染指令 v-for
5.3.1 基本用法
當(dāng)需要將一個數(shù)組遍歷或枚舉一個對象循環(huán)顯示時,就會用到列表渲染指令v-for。
它的表達式需結(jié)合in來使用,類似item in items的形式。
看下面的代碼:
?
我們定義一個數(shù)組類型的數(shù)據(jù)books,用v-for將<li>標(biāo)簽循環(huán)渲染。
效果如圖5-3所示:

在表達式中,books是數(shù)據(jù),book是當(dāng)前數(shù)組元素的別名,循環(huán)出的每個<li>內(nèi)的元素都可以訪問到對應(yīng)的當(dāng)前數(shù)據(jù)book。
列表渲染也支持用of來代替in作為分隔符,它更接近JavaScript迭代器的語法:
?
v-for的表達式支持一個可選參數(shù)作為當(dāng)前項的索引,例如:
1 <div id="app"> 2 <ul> 3 <li v-for="(book, index) in books">{{index}} - {{book.name}}</li> 4 </ul> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 books: [ 12 { name: "《Vue.js實戰(zhàn)》" }, 13 { name: "《JavaScript語言精粹》" }, 14 { name: "《JavaScript高級程序設(shè)計》" } 15 ] 16 } 17 }); 18 </script>?
分隔符in前的語句使用括號,第二項就是books當(dāng)前項的索引。
渲染后的結(jié)果如圖5-4所示:

提示:
如果你使用過Vue.js 1.x的版本,這里的index也可以由內(nèi)置的$index代替,不過在2.x里取消了該用法。
與v-if一樣,v-for也可以用在內(nèi)置標(biāo)簽<template>上,將多個元素進行渲染:
1 <div id="app"> 2 <ul> 3 <template v-for="book in books"> 4 <li>書名:{{book.name}}</li> 5 <li>作者:{{book.author}}</li> 6 </template> 7 </ul> 8 </div> 9 10 <script> 11 var app = new Vue({ 12 el: "#app", 13 data: { 14 books: [ 15 { name: "《Vue.js實戰(zhàn)》", author:"梁灝" }, 16 { name: "《JavaScript語言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高級程序設(shè)計》", author:"Nicholas C.Zakas" } 18 ] 19 } 20 }); 21 </script>?
除了數(shù)組外,對象的屬性也是可以遍歷的。
例如:
?
渲染后的結(jié)果為:
1 <div id="app"> 2 <span>Aresn</span> 3 <span>男</span> 4 <span>26</span> 5 </div>?
遍歷對象屬性時,有兩個可選參數(shù),分別是鍵名和索引:
1 <div id="app"> 2 <ul> 3 <li v-for="(value, key, index) in user"> 4 {{index}} - {{key}} - {{value}} 5 </li> 6 </ul> 7 </div> 8 9 <script> 10 var app = new Vue({ 11 el: "#app", 12 data: { 13 user: { 14 name: "Aresn", 15 gender: "男", 16 age: 26 17 } 18 } 19 }); 20 </script>?
渲染后的結(jié)果如圖5-5所示:

v-for還可以迭代整數(shù):
1 <div id="app"> 2 <span v-for="n in 10">{{n}} </span> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app" 8 }); 9 </script>?
渲染后的結(jié)果為:
1 <div id="app"> 2 <span>1 </span> 3 <span>2 </span> 4 <span>3 </span> 5 <span>4 </span> 6 <span>5 </span> 7 <span>6 </span> 8 <span>7 </span> 9 <span>8 </span> 10 <span>9 </span> 11 <span>10 </span> 12 </div>?
5.3.2 數(shù)組更新
Vue的核心是數(shù)據(jù)與視圖的雙向綁定,當(dāng)我們修改數(shù)組時,Vue會檢測到數(shù)據(jù)變化,所以用v-for渲染的視圖也會立即更新。
Vue包含了一組觀察數(shù)組變異的方法,使用它們改變數(shù)組也會觸發(fā)視圖更新:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
例如,我們將之前一個示例的數(shù)據(jù)books添加一項:
1 app.books.push({ 2 name: "《CSS解密》", 3 author: "[希] Lea Verou" 4 });?
可以嘗試編寫完整示例來查看效果。
使用以上方法會改變被這些方法調(diào)用的原始數(shù)組,有些方法不會改變原數(shù)組,例如:
- filter()
- concat()
- slice()
它們返回的是一個新數(shù)組,在使用這些非變異方法時,可以用新數(shù)組來替換原數(shù)組。
還是之前展示節(jié)目的示例,我們找出含有JavaScript關(guān)鍵詞的書目,例如:
?
渲染的結(jié)果中,第一項《Vue.js實戰(zhàn)》被過濾掉了,只顯示了書名中含有JavaScript的選項。
Vue在檢測到數(shù)組變化時,并不是直接重新渲染整個列表,而是最大化地復(fù)用DOM元素。
替換的數(shù)組中,含有相同元素的項不會被重新渲染,因此可以大膽地用新數(shù)組來替換就數(shù)組,不用擔(dān)心性能問題。
需要注意的是,以下變動的數(shù)組中,Vue是不能檢測到的,也不會觸發(fā)視圖更新:
- 通過索引直接設(shè)置項,比如app.book[3] = {...}
- 修改數(shù)組長度,比如app.books.length=1
解決第一個問題可以用兩種方法實現(xiàn)同樣的效果,第一種是使用Vue內(nèi)置的set方法:
1 Vue.set(app.books, 2, { 2 name: "《CSS揭秘》", 3 author: "[希] Lea Verou" 4 });?
如果是在webpack中使用組件化的方式(進階篇中將介紹),默認(rèn)是沒有導(dǎo)入Vue的,這時可以使用$set。
例如:
?
這里的this指向的就是當(dāng)前組件的實例,即app。
在非webpack模式下也可以用$set方法,例如app.$set(...)。
另一種方法:
1 app.books.splice(2, 1, { 2 name: "《CSS揭秘》", 3 author: "[希] Lea Verou" 4 });?
第二個問題也可以直接用splice來解決:
1 app.books.splice(1);?
5.3.3 過濾與排序
當(dāng)你不想改變原數(shù)組,想通過一個數(shù)組的副本來做過濾或排序的顯示時,可以使用計算屬性來返回過濾或排序后的數(shù)組。
例如:
?
上例是把書名中包含"JavaScript"關(guān)鍵詞的數(shù)據(jù)過濾出來,計算屬性filterBooks依賴books,但是不會修改books。
實現(xiàn)排序也是類似的,比如在此基礎(chǔ)上新加一個計算屬性sortedBooks,按照書名的長度由長到短進行排序:
?
提示:
在Vue.js 2.x中廢棄了1.x中內(nèi)置的limitBy、filterBy和orderBy過濾器,統(tǒng)一改用計算屬性來實現(xiàn)。
5.4 方法與事件
5.4.1 基本用法
在第2.2節(jié),我們已經(jīng)引入了Vue事件處理的概念v-on。
在事件綁定上,類似原生JavaScript的onclick等寫法,也是在HTML上進行監(jiān)昕的。
例如,我們監(jiān)昕一個按鈕的點擊事件,設(shè)置一個計數(shù)器,每次點擊都加1:
?
提示:
上面的@click等同于v-on:click,是一個語法糖,如不特殊說明,后面都將使用語法糖寫法,可以回顧第2.3章節(jié)。
@click的表達式可以直接使用JavaScript語句,也可以是一個在Vue實例中methods選項內(nèi)的函數(shù)名。
比如對上例進行擴展,再增加一個按鈕,點擊一次,計數(shù)器累加10:
?
在methods中定義了我們需要的方法供@click調(diào)用,需要注意的是,@click調(diào)用的方法名后可以不跟括號()。
此時,如果該方法有參數(shù),默認(rèn)會將原生事件對象event傳入,可以嘗試修改為@click="handleAdd",然后在handleAdd內(nèi)打印出count參數(shù)看看。
在大部分業(yè)務(wù)場景中,如果方法不需要傳入?yún)?shù),為了漸變可以不寫括號。
這種在HTML元素上監(jiān)聽事件的設(shè)計看似將DOM與JavaScript緊耦合,違背分離的原理,實則剛好相反。
因為通過HTML就可以知道調(diào)用的是哪個方法,講邏輯與DOM接口,便于維護。
最重要的是,當(dāng)ViewModel銷毀時,所有的事件處理器都會自動刪除,無需自己清理。
Vue提供了一個特殊變量$event,用于訪問原生DOM事件,例如下面的實例可以阻止連接打開:
1 <div id="app"> 2 <a href="http://www.baidu.com" @click="handleClick('禁止打開', $event)">打開連接</a> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 methods: { 9 handleClick: function(message, event) { 10 event.preventDefault(); 11 alert(message); 12 } 13 } 14 }); 15 </script>?
5.4.2 修飾符
在上例使用的event.preventDefault()也可以用Vue事件的修飾符來實現(xiàn)。
在@綁定的事件后加小圓點.,在跟一個后綴來使用修飾符。
Vue支持以下修飾符:
- .stop
- .prevent
- .capture
- .self
- .once
具體用法如下:
1 <!-- 阻止事件冒泡 --> 2 <a @click.stop="handle"></a> 3 4 <!-- 提交事件不再重載頁面 --> 5 <form @submit.prevent="handle"></form> 6 7 <!-- 修飾符可以串聯(lián) --> 8 <a @click.stop.prevent="handle"></a> 9 10 <!-- 只有修飾符 --> 11 <form @submit.prevent></form> 12 13 <!-- 添加事件偵聽器時使用事件捕獲模式 --> 14 <div @click.capture="handle">...</div> 15 16 <!-- 只當(dāng)事件在該元素本身(而不是子元素)觸發(fā)時觸發(fā)回調(diào) --> 17 <div @click.self="handle">...</div> 18 19 <!-- 只觸發(fā)一次,組件同樣適用 --> 20 <div @click.once="handle">...</div>?
在表單元素上監(jiān)聽鍵盤事件時,還可以使用按鍵修飾符。
比如按下具體某個鍵時才調(diào)用方法:
?
也可以自己配置具體按鍵:
1 Vue.config.keyCodes.f1 = 112; 2 // 全局定義后,就可以使用@keyup.f1?
除了具體的某個keyCode外,Vue還提供了一些快捷名稱。
以下是全部的別名:
- .enter
- .tab
- .delete(捕獲"刪除"和"退格"鍵)
- .esc
- .space
- .up
- .down
- .left
- .right
這些按鍵修飾符也可以組合使用,或和鼠標(biāo)一起使用:
- .ctrl
- .alt
- .shift
- .meta(Mac下是Command鍵,Windows下是窗口鍵)
例如:
1 <!-- Shift + S --> 2 <input @keyup.shift.83="handleSave"> 3 4 <!-- Ctrl + Click --> 5 <div @click.ctrl="doSomething">Do Smothing</div>?
以上就是事件指令v-on的基本內(nèi)容,在第7章的組件中,我們還將介紹用v-on來綁定自定義事件。
5.5 實戰(zhàn):利用計算屬性、指令等知識開發(fā)購物車
前五章內(nèi)容基本涵蓋了Vue.js最核心的常用的知識點,掌握這些內(nèi)容已經(jīng)可以上手開發(fā)一些小功能了。
本節(jié)則以Vue.js的計算屬性、內(nèi)置指令、方法等內(nèi)容為基礎(chǔ),完成一個在業(yè)務(wù)中具有代表性的小功能:購物車。
在開始寫代碼前,要對需求進行分析,這樣有助于我們理清業(yè)務(wù)邏輯,盡可能還原設(shè)計產(chǎn)品交互。
購物車需要展示一個已加入購物車的商品列表,包含商品名稱、商品單價、購買數(shù)量和操作等信息,還需要實時顯示購買的總價。
其中購買數(shù)量可以增加或減少,每類商品還可以從購物車中移除。
最終實現(xiàn)的效果如圖5-6所示:

在明確需求后,我們就可以開始編程了。
因為業(yè)務(wù)代碼較多,這次我們將HTML、CSS、JavaScript分離為3個文件,便于閱讀和維護:
- index.html (引入資源及模板)
- index.js (Vue實例及業(yè)務(wù)代碼)
- index.css (樣式)
現(xiàn)在index.html中引入Vue.js和相關(guān)資源,創(chuàng)建一個根元素來掛在Vue實例:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>購物車示例</title> 8 <link rel="stylesheet" href="index.css"> 9 </head> 10 <body> 11 <div id="app" v-cloak></div> 12 13 <script src="https://unpkg.com/vue/dist/vue.min.js"></script> 14 <script src="index.js"></script> 15 </body> 16 </html>?
注意,這里將vue.min.js和index.js文件寫在<body>的最底部,如果寫在<head>里,Vue實例將無法創(chuàng)建,因為此時DOM還沒有被解析完成,除非通過異步或在事件DOMContentLoaded(IE是onreadystatechange)觸發(fā)時再創(chuàng)建Vue實例,這有點像jQuery的$(document).ready()方法。
本例需要用到Vue.js的computed、methods等選項,在index.js中縣初始化實例:
1 var app = new Vue({ 2 el: "#app", 3 data: {}, 4 computed: {}, 5 methods: {} 6 });?
我們需要的數(shù)據(jù)比較簡單,只有一個列表,里面包含了商品名稱、單價、購買數(shù)量。
在實際業(yè)務(wù)中,這個列表應(yīng)該是通過Ajax從服務(wù)端動態(tài)獲取的,這里只做示例,所以直接寫入在data選項內(nèi),另外每個商品還應(yīng)該有一個全局唯一的id。
我們在data內(nèi)寫入列表list:
?
數(shù)據(jù)構(gòu)建好后,可以在index.html中展示列表了,毫無疑問,肯定會用到v-for,不過在此之前,我們先做一些小的優(yōu)化。
因為每個商品都是可以從購物車移除的,所以當(dāng)列表為空時,在頁面中顯示一個”購物車為空“的提示更為友好,我們可以通過判斷數(shù)組list的長度來實現(xiàn)該功能:
?
<template>里的代碼分兩部分,一部分是商品列表信息,我們用表格<table>來展現(xiàn);
另一部分就是帶有千位分隔符的商品總價(每隔三位數(shù)加一個逗號)。
這部分代碼如下:
?
總價totalPrice是依賴于商品列表而動態(tài)變化的,所以我們用計算屬性來實現(xiàn),順便將結(jié)果轉(zhuǎn)換為帶有"千位分隔符"的數(shù)字。
在index.js的computed選項內(nèi)寫入:
?
這段代碼難點在于千位分隔符的轉(zhuǎn)換,大家可以查閱正則匹配的相關(guān)內(nèi)容后嘗試了解replace()的正則含義。
最后就剩下商品列表的渲染和相關(guān)的幾個操作了。
現(xiàn)在<body>內(nèi)把數(shù)組list用v-for指令循環(huán)出來:
?
商品序號、名稱、單價、數(shù)量都是直接使用插值來完成的,在第4列的兩個按鈕<button>用于增/減購買數(shù)量,分別綁定了兩個方法handleReduce和handleAdd,參數(shù)都是當(dāng)前商品在數(shù)組list中的索引。
很多時候,一個元素上會同時使用多個特性(尤其是在組件中使用props傳遞數(shù)據(jù)時),寫在一行代碼較長,不便閱讀,所以建議特性過多時,將每個特性都單獨寫為一行,比如第一個<button>中使用了v-bind和v-on兩個指令(這里都用的語法糖寫法)。
每件商品購買數(shù)量最少是1件,所以當(dāng)count為1時,不允許再繼續(xù)減少,所以這里給<button>動態(tài)綁定了disabled特性來禁用按鈕。
在index.js中繼續(xù)完成剩余的3個方法:
1 methods: { 2 handleReduce: function (index) { 3 if (this.list[index].count === 1) return; 4 this.list[index].count--; 5 }, 6 handleAdd: function (index) { 7 this.list[index].count++; 8 }, 9 handleRemove: function (index) { 10 this.list.splice(index, 1); 11 } 12 }?
這3個方法都是直接對數(shù)組list的操作,沒有太復(fù)雜的邏輯。
需要說明的是,雖然在<button>上已經(jīng)綁定了disabled特性,但是在handleReduce方法內(nèi)有判斷了以便,這是因為在某些時刻,可能不一定會用<button>元素,也可能是div、span等,給它們增加disabled是沒有任何作用的,所以安全起見,在業(yè)務(wù)邏輯中在判斷一次,避免因修改HTML模板后出現(xiàn)bug。
一下是購物車示例的完整代碼:
index.html:
?
index.js:
1 var app = new Vue({ 2 el: "#app", 3 data: { 4 list: [ 5 {id: 1, name: "iPhone 7", price: 6188, count: 1}, 6 {id: 2, name: "iPad Pro", price: 5888, count: 1}, 7 {id: 3, name: "McaBook Pro", price: 21488, count: 1} 8 ] 9 }, 10 computed: { 11 totalPrice: function () { 12 var total = 0; 13 for (var i = 0; i < this.list.length; i++) { 14 var item = this.list[i]; 15 total += item.price * item.count; 16 } 17 return total.toString().replace(/\B(?=(\d{3})+$)/g, ","); 18 } 19 }, 20 methods: { 21 handleReduce: function (index) { 22 if (this.list[index].count === 1) return; 23 this.list[index].count--; 24 }, 25 handleAdd: function (index) { 26 this.list[index].count++; 27 }, 28 handleRemove: function (index) { 29 this.list.splice(index, 1); 30 } 31 } 32 });?
index.css:
1 [v-cloak]{display:none;} 2 table{border:1px solid #E9E9E9; border-collapse:collapse; border-spacing:0; empty-cells:show;} 3 th, td{padding:8px 16px; border:1px solid #E9E9E9; text-align:left;} 4 th{background:#F7F7F7; color:#5C6B77; font-weight:600; white-space:nowrap;}?
練習(xí)1:在當(dāng)前示例基礎(chǔ)上擴展商品列表,新增一項是否選中該商品的功能,總價變?yōu)橹挥嬎氵x中商品的總價,同時提供一個全選的按鈕。
練習(xí)2:將商品列表list改為一個二維數(shù)組來實現(xiàn)商品的分類,比如可分為"電子產(chǎn)品"、"生活用品"和"果蔬",同類商品聚合在一起。提示,你可能會用到兩次v-for。
?
轉(zhuǎn)載于:https://www.cnblogs.com/geeksss/p/10776465.html
總結(jié)
以上是生活随笔為你收集整理的Vue.js 学习笔记 第5章 内置指令的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python接口自动化(二十四)--un
- 下一篇: Windows下的命令神器Cmder