本篇目錄:
5.1 基本指令css
5.2 條件渲染指令html
5.4 方法與事件webpack
回顧一下第2.2節,咱們己經介紹過指令(Directive)的概念了,Vue.js的指令是帶有特殊前綴v-
的HTML特性,它綁定一個表達式,並將一些特性應用到DOM上。
其實咱們已經用到過不少Vue內置的指令,好比v-html
、v-pre
,還有上一章的v-bind
。
本章將繼續介紹Vue.js中更多經常使用的內置指令。vue-router
v-cloak
不須要表達式,它會在Vue實例結束編譯時從綁定的HTML元素上移除,常常和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>
這時雖然已經加了指令v-cloak
,但其實並無起到任何做用。
當網速較慢、Vue.js文件還沒加載完時,在頁面上會顯示{{message}}
的字樣。
直到Vue建立實例、編譯模板時,DOM纔會被替換,因此這個過程屏幕是有閃動的。
只要加一句CSS就能夠解決這個問題了:數組
1 <style> 2 [v-cloak] { 3 display: none; 4 } 5 </style>
在通常狀況下,v-cloak
是一個解決初始化慢致使頁面閃動的最佳實踐,對於簡單的項目很實用,可是在具備工程化的項目裏,好比後面進階篇將介紹webpack和vue-router時,項目的HTML結構只有一個空的div元素,剩餘的內容都是由路由去掛載不一樣組件完成的,因此再也不須要v-cloak
。安全
v-once
也是一個不須要表達式的指令,做用是定義它的元素或組件只渲染一次,包括元素或組件的全部子節點。
首次渲染後,再也不隨數據的變化從新渲染,將被視爲靜態內容,例如:app
1 <div id="app"> 2 <span v-once>{{message}}</span> 3 <div v-once> 4 <span>{{message}}</span> 5 </div> 6 </div> 7 8 <script> 9 var app = new Vue({ 10 el: "#app", 11 data: { 12 message: "這是一段文本" 13 } 14 }); 15 </script>
v-once
在業務中也不多使用,當你須要進一步優化性能時,可能會用到。
與JavaScript的條件語句if
、else
、else if
相似,Vue.js的條件指令能夠根據表達式的值在DOM中渲染或銷燬元素/組件。
例如:
1 <div id="app"> 2 <p v-if="status===1">當status爲1時顯示此行</p> 3 <p v-else-if="status===2">當status爲2時顯示此行</p> 4 <p v-else>不然顯示此行</p> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 status: 1 12 } 13 }); 14 </script>
v-else-if
要緊跟v-if
,v-else
要緊跟v-else-if
或v-if
。
表達式的值爲true
時,當前元素/組件及全部子節點將被渲染,爲false
時被移除。
若是一次判斷的是多個元素,能夠在Vue.js內置的<template>
元素上使用條件指令,最終渲染的結果不包含鈣元素。
例如:
1 <div id="app"> 2 <template v-if="status===1"> 3 <p>這是一段文本</p> 4 <p>這是一段文本</p> 5 <p>這是一段文本</p> 6 </template> 7 </div> 8 9 <script> 10 var app = new Vue({ 11 el: "#app", 12 data: { 13 status: 1 14 } 15 }); 16 </script>
Vue在渲染元素時,出於效率考慮,會盡量地複用已有的元素而非從新渲染。
好比下面的示例:
1 <div id="app"> 2 <template v-if="type==='name'"> 3 <label for="name">用戶名:</label> 4 <input type="text" id="name" name="name" placeholder="請輸入用戶名"> 5 </template> 6 <template v-else> 7 <label for="mail">郵箱:</label> 8 <input type="text" id="mail" name="mail" placeholder="請輸入郵箱"> 9 </template> 10 <button type="button" @click="handleToggleClick">切換輸入類型</button> 11 </div> 12 13 <script> 14 var app = new Vue({ 15 el: "#app", 16 data: { 17 type: "name" 18 }, 19 methods: { 20 handleToggleClick: function() { 21 this.type = this.type === "name" ? "mail" : "name"; 22 } 23 } 24 }); 25 </script>
如圖5-1和圖5-2所示,鍵入內容後,點擊切換按鈕,雖然DOM變了,可是以前在輸入框鍵入的內容並無改變,只是替換了placeholder的內容,說明<input>
元素被複用了。


若是你不但願這樣作,可使用Vue.js提供的key屬性,它可讓你本身決定是否要複用元素,key的值必須是惟一的。
例如:
1 <div id="app"> 2 <template v-if="type==='name'"> 3 <label for="name">用戶名:</label> 4 <input type="text" id="name" name="name" key="name-input" placeholder="請輸入用戶名"> 5 </template> 6 <template v-else> 7 <label for="mail">郵箱:</label> 8 <input type="text" id="mail" name="mail" key="mail-input" placeholder="請輸入郵箱"> 9 </template> 10 <button type="button" @click="handleToggleClick">切換輸入類型</button> 11 </div> 12 13 <script> 14 var app = new Vue({ 15 el: "#app", 16 data: { 17 type: "name" 18 }, 19 methods: { 20 handleToggleClick: function() { 21 this.type = this.type === "name" ? "mail" : "name"; 22 } 23 } 24 }); 25 </script>
給兩個<input>
元素都增長key
後,就不會複用了,切換類型時鍵入的內容也會被刪除,不過<label>
元素仍然是被複用的,由於沒有添加key
屬性。
v-show
的用法與v-if
基本一致,只不過v-show
是改變元素的CSS屬性display
。
當v-show
表達式的值爲false
時,元素會隱藏,查看DOM結構會看到元素上加載了內聯樣式display:none
。
例如:
1 <div id="app"> 2 <p v-show="status===1">當status爲1時顯示此行</p> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 status: 2 10 } 11 }); 12 </script>
渲染後的結果爲:
1 <div id="app"> 2 <p style="display: none;">當status爲1時顯示此行</p> 3 </div>
提示:
v-show
不能在<template>
上使用。
v-if
和v-show
具備相似的功能,不過v-if
纔是真正的條件渲染,它會根據表達式適當地銷燬或重建元素及綁定的事件或子組件。
若表達式初始值爲false
,則一開始元素/組件並不會渲染,只有當條件第一次變爲true
時纔開始編譯。
而v-show
只是簡單的CSS屬性切換,不管條件真與否,都會被編譯。
相比之下,v-if
更適合條件不常常改變的場景,由於它切換開銷相對較大,而v-show
適用於頻繁切換條件。
當須要將一個數組遍歷或枚舉一個對象循環顯示時,就會用到列表渲染指令v-for
。
它的表達式需結合in
來使用,相似item in items
的形式。
看下面的代碼:
1 <div id="app"> 2 <ul> 3 <li v-for="book in books">{{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實戰》" }, 13 { name: "《JavaScript語言精粹》" }, 14 { name: "《JavaScript高級程序設計》" } 15 ] 16 } 17 }); 18 </script>
咱們定義一個數組類型的數據books
,用v-for
將<li>
標籤循環渲染。
效果如圖5-3所示:

在表達式中,books
是數據,book
是當前數組元素的別名,循環出的每一個<li>
內的元素均可以訪問到對應的當前數據book
。
列表渲染也支持用of
來代替in
做爲分隔符,它更接近JavaScript迭代器的語法:
1 <li v-for="book of books">{{book.name}}</li>
v-for
的表達式支持一個可選參數做爲當前項的索引,例如:
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實戰》" }, 13 { name: "《JavaScript語言精粹》" }, 14 { name: "《JavaScript高級程序設計》" } 15 ] 16 } 17 }); 18 </script>
分隔符in
前的語句使用括號,第二項就是books
當前項的索引。
渲染後的結果如圖5-4所示:

提示:
若是你使用過Vue.js 1.x的版本,這裏的index
也能夠由內置的$index
代替,不過在2.x裏取消了該用法。
與v-if
同樣,v-for
也能夠用在內置標籤<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實戰》", author:"梁灝" }, 16 { name: "《JavaScript語言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高級程序設計》", author:"Nicholas C.Zakas" } 18 ] 19 } 20 }); 21 </script>
除了數組外,對象的屬性也是能夠遍歷的。
例如:
1 <div id="app"> 2 <span v-for="value in user">{{value}}</span> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 user: { 10 name: "Aresn", 11 gender: "男", 12 age: 26 13 } 14 } 15 }); 16 </script>
渲染後的結果爲:
1 <div id="app"> 2 <span>Aresn</span> 3 <span>男</span> 4 <span>26</span> 5 </div>
遍歷對象屬性時,有兩個可選參數,分別是鍵名和索引:
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>
渲染後的結果如圖5-5所示:

v-for
還能夠迭代整數:
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>
渲染後的結果爲:
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>
Vue的核心是數據與視圖的雙向綁定,當咱們修改數組時,Vue會檢測到數據變化,因此用v-for
渲染的視圖也會當即更新。
Vue包含了一組觀察數組變異的方法,使用它們改變數組也會觸發視圖更新:
例如,咱們將以前一個示例的數據books
添加一項:
1 app.books.push({ 2 name: "《CSS解密》", 3 author: "[希] Lea Verou" 4 });
能夠嘗試編寫完整示例來查看效果。
使用以上方法會改變被這些方法調用的原始數組,有些方法不會改變原數組,例如:
它們返回的是一個新數組,在使用這些非變異方法時,能夠用新數組來替換原數組。
仍是以前展現節目的示例,咱們找出含有JavaScript
關鍵詞的書目,例如:
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實戰》", author:"梁灝" }, 16 { name: "《JavaScript語言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高級程序設計》", author:"Nicholas C.Zakas" } 18 ], 19 } 20 }); 21 22 app.books = app.books.filter(function(item) { 23 return item.name.match(/JavaScript/); 24 }); 25 </script>
渲染的結果中,第一項《Vue.js實戰》被過濾掉了,只顯示了書名中含有JavaScript的選項。
Vue在檢測到數組變化時,並非直接從新渲染整個列表,而是最大化地複用DOM元素。
替換的數組中,含有相同元素的項不會被從新渲染,所以能夠大膽地用新數組來替換就數組,不用擔憂性能問題。
須要注意的是,如下變更的數組中,Vue是不能檢測到的,也不會觸發視圖更新:
app.book[3] = {...}
app.books.length=1
解決第一個問題能夠用兩種方法實現一樣的效果,第一種是使用Vue內置的set方法:
1 Vue.set(app.books, 2, { 2 name: "《CSS揭祕》", 3 author: "[希] Lea Verou" 4 });
若是是在webpack中使用組件化的方式(進階篇中將介紹),默認是沒有導入Vue的,這時可使用$set
。
例如:
1 this.$set(app.books, 2, { 2 name: "《CSS揭祕》", 3 author: "[希] Lea Verou" 4 });
這裏的this
指向的就是當前組件的實例,即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);
當你不想改變原數組,想經過一個數組的副原本作過濾或排序的顯示時,可使用計算屬性來返回過濾或排序後的數組。
例如:
1 <div id="app"> 2 <ul> 3 <template v-for="book in filterBooks"> 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實戰》", author:"梁灝" }, 16 { name: "《JavaScript語言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高級程序設計》", author:"Nicholas C.Zakas" } 18 ], 19 }, 20 computed: { 21 filterBooks: function () { 22 return this.books.filter(function (book) { 23 return book.name.match(/JavaScript/); 24 }); 25 } 26 } 27 }); 28 </script>
上例是把書名中包含"JavaScript"關鍵詞的數據過濾出來,計算屬性filterBooks
依賴books
,可是不會修改books
。
實現排序也是相似的,好比在此基礎上新加一個計算屬性sortedBooks
,按照書名的長度由長到短進行排序:
1 computed: { 2 sortedBooks: function () { 3 return this.books.sort(function (a, b) { 4 return a.name.length < b.name.length; 5 }); 6 } 7 }
提示:
在Vue.js 2.x中廢棄了1.x中內置的limitBy
、filterBy
和orderBy
過濾器,統一改用計算屬性來實現。
在第2.2節,咱們已經引入了Vue事件處理的概念v-on
。
在事件綁定上,相似原生JavaScript的onclick
等寫法,也是在HTML上進行監昕的。
例如,咱們監昕一個按鈕的點擊事件,設置一個計數器,每次點擊都加1:
1 <div id="app"> 2 點擊次數: {{counter}} 3 <button type="button" @click="counter++">累加1</button> 4 </div> 5 6 <script> 7 var app = new Vue({ 8 el: "#app", 9 data: { 10 counter: 0 11 } 12 }); 13 </script>
提示:
上面的@click
等同於v-on:click
,是一個語法糖,如不特殊說明,後面都將使用語法糖寫法,能夠回顧第2.3章節。
@click
的表達式能夠直接使用JavaScript語句,也能夠是一個在Vue實例中methods
選項內的函數名。
好比對上例進行擴展,再增長一個按鈕,點擊一次,計數器累加10:
1 <div id="app"> 2 點擊次數: {{counter}} 3 <button type="button" @click="handleAdd()">累加1</button> 4 <button type="button" @click="handleAdd(10)">累加10</button> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 counter: 0 12 }, 13 methods: { 14 handleAdd: function(count) { 15 count = count || 1; 16 // this指向當前Vue實例app 17 this.counter += count; 18 } 19 } 20 }); 21 </script>
在methods
中定義了咱們須要的方法供@click
調用,須要注意的是,@click
調用的方法名後能夠不跟括號()
。
此時,若是該方法有參數,默認會將原生事件對象event
傳入,能夠嘗試修改成@click="handleAdd"
,而後在handleAdd
內打印出count
參數看看。
在大部分業務場景中,若是方法不須要傳入參數,爲了漸變能夠不寫括號。
這種在HTML元素上監聽事件的設計看似將DOM與JavaScript緊耦合,違背分離的原理,實則恰好相反。
由於經過HTML就能夠知道調用的是哪一個方法,講邏輯與DOM接口,便於維護。
最重要的是,當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>
在上例使用的event.preventDefault()
也能夠用Vue事件的修飾符來實現。
在@
綁定的事件後加小圓點.
,在跟一個後綴來使用修飾符。
Vue支持如下修飾符:
具體用法以下:
1 <!-- 阻止事件冒泡 --> 2 <a @click.stop="handle"></a> 3 4 <!-- 提交事件再也不重載頁面 --> 5 <form @submit.prevent="handle"></form> 6 7 <!-- 修飾符能夠串聯 --> 8 <a @click.stop.prevent="handle"></a> 9 10 <!-- 只有修飾符 --> 11 <form @submit.prevent></form> 12 13 <!-- 添加事件偵聽器時使用事件捕獲模式 --> 14 <div @click.capture="handle">...</div> 15 16 <!-- 只當事件在該元素自己(而不是子元素)觸發時觸發回調 --> 17 <div @click.self="handle">...</div> 18 19 <!-- 只觸發一次,組件一樣適用 --> 20 <div @click.once="handle">...</div>
在表單元素上監聽鍵盤事件時,還可使用按鍵修飾符。
好比按下具體某個鍵時才調用方法:
1 <!-- 只有在keyCode是13時調用 vm.submit() --> 2 <input @keyup.13="submit">
也能夠本身配置具體按鍵:
1 Vue.config.keyCodes.f1 = 112; 2 // 全局定義後,就可使用@keyup.f1
除了具體的某個keyCode外,Vue還提供了一些快捷名稱。
如下是所有的別名:
這些按鍵修飾符也能夠組合使用,或和鼠標一塊兒使用:
例如:
1 <!-- Shift + S --> 2 <input @keyup.shift.83="handleSave"> 3 4 <!-- Ctrl + Click --> 5 <div @click.ctrl="doSomething">Do Smothing</div>
以上就是事件指令v-on
的基本內容,在第7章的組件中,咱們還將介紹用v-on
來綁定自定義事件。
前五章內容基本涵蓋了Vue.js最核心的經常使用的知識點,掌握這些內容已經能夠上手開發一些小功能了。
本節則以Vue.js的計算屬性、內置指令、方法等內容爲基礎,完成一個在業務中具備表明性的小功能:購物車。
在開始寫代碼前,要對需求進行分析,這樣有助於咱們理清業務邏輯,儘量還原設計產品交互。
購物車須要展現一個已加入購物車的商品列表,包含商品名稱、商品單價、購買數量和操做等信息,還須要實時顯示購買的總價。
其中購買數量能夠增長或減小,每類商品還能夠從購物車中移除。
最終實現的效果如圖5-6所示:

在明確需求後,咱們就能夠開始編程了。
由於業務代碼較多,此次咱們將HTML、CSS、JavaScript分離爲3個文件,便於閱讀和維護:
如今index.html中引入Vue.js和相關資源,建立一個根元素來掛在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實例將沒法建立,由於此時DOM尚未被解析完成,除非經過異步或在事件DOMContentLoaded(IE是onreadystatechange
)觸發時再建立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 });
咱們須要的數據比較簡單,只有一個列表,裏面包含了商品名稱、單價、購買數量。
在實際業務中,這個列表應該是經過Ajax從服務端動態獲取的,這裏只作示例,因此直接寫入在data
選項內,另外每一個商品還應該有一個全局惟一的id
。
咱們在data
內寫入列表list
:
1 data: { 2 list: [ 3 {id: 1, name: "iPhone 7", price: 6188, count: 1}, 4 {id: 2, name: "iPad Pro", price: 5888, count: 1}, 5 {id: 3, name: "McaBook Pro", price: 21488, count: 1} 6 ] 7 }
數據構建好後,能夠在index.html
中展現列表了,毫無疑問,確定會用到v-for
,不過在此以前,咱們先作一些小的優化。
由於每一個商品都是能夠從購物車移除的,因此當列表爲空時,在頁面中顯示一個」購物車爲空「的提示更爲友好,咱們能夠經過判斷數組list
的長度來實現該功能:
1 <div id="app" v-cloak> 2 <template v-if="list.length"></template> 3 <div v-else>購物車爲空</div> 4 </div>
<template>
裏的代碼分兩部分,一部分是商品列表信息,咱們用表格<table>
來展示;
另外一部分就是帶有千位分隔符的商品總價(每隔三位數加一個逗號)。
這部分代碼以下:
1 <template v-if="list.length"> 2 <table> 3 <thead> 4 <tr> 5 <th></th> 6 <th>商品名稱</th> 7 <th>商品單價</th> 8 <th>購買數量</th> 9 <th>操做</th> 10 </tr> 11 </thead> 12 <tbody> 13 </tbody> 14 </table> 15 <div>總價:¥ {{totalPrice}}</div> 16 </template>
總價totalPrice
是依賴於商品列表而動態變化的,因此咱們用計算屬性來實現,順便將結果轉換爲帶有"千位分隔符"的數字。
在index.js
的computed
選項內寫入:
1 computed: { 2 totalPrice: function () { 3 var total = 0; 4 for (var i = 0; i < this.list.length; i++) { 5 var item = this.list[i]; 6 total += item.price * item.count; 7 } 8 return total.toString().replace(/\B(?=(\d{3})+$)/g, ","); 9 } 10 }
這段代碼難點在於千位分隔符的轉換,你們能夠查閱正則匹配的相關內容後嘗試瞭解replace()
的正則含義。
最後就剩下商品列表的渲染和相關的幾個操做了。
如今<body>
內把數組list
用v-for
指令循環出來:
1 <tbody> 2 <tr v-for="(item, index) in list"> 3 <td>{{index + 1}}</td> 4 <td>{{item.name}}</td> 5 <td>{{item.price}}</td> 6 <td> 7 <button @click="handleReduce(index)" :disabled="item.count===1">-</button> 8 {{item.count}} 9 <button @click="handleAdd(index)">+</button> 10 </td> 11 <td> 12 <button @click="handleRemove(index)">移除</button> 13 </td> 14 </tr> 15 </tbody>
商品序號、名稱、單價、數量都是直接使用插值來完成的,在第4列的兩個按鈕<button>
用於增/減購買數量,分別綁定了兩個方法handleReduce
和handleAdd
,參數都是當前商品在數組list
中的索引。
不少時候,一個元素上會同時使用多個特性(尤爲是在組件中使用props
傳遞數據時),寫在一行代碼較長,不便閱讀,因此建議特性過多時,將每一個特性都單獨寫爲一行,好比第一個<button>
中使用了v-bind
和v-on
兩個指令(這裏都用的語法糖寫法)。
每件商品購買數量最少是1件,因此當count
爲1時,不容許再繼續減小,因此這裏給<button>
動態綁定了disabled
特性來禁用按鈕。
在index.js
中繼續完成剩餘的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個方法都是直接對數組list
的操做,沒有太複雜的邏輯。
須要說明的是,雖然在<button>
上已經綁定了disabled
特性,可是在handleReduce
方法內有判斷了以便,這是由於在某些時刻,可能不必定會用<button>
元素,也多是div、span等,給它們增長disabled
是沒有任何做用的,因此安全起見,在業務邏輯中在判斷一次,避免因修改HTML模板後出現bug。
一下是購物車示例的完整代碼:
index.html:
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> 12 <template v-if="list.length"> 13 <table> 14 <thead> 15 <tr> 16 <th></th> 17 <th>商品名稱</th> 18 <th>商品單價</th> 19 <th>購買數量</th> 20 <th>操做</th> 21 </tr> 22 </thead> 23 <tbody> 24 <tr v-for="(item, index) in list"> 25 <td>{{index + 1}}</td> 26 <td>{{item.name}}</td> 27 <td>{{item.price}}</td> 28 <td> 29 <button @click="handleReduce(index)" :disabled="item.count===1">-</button> 30 {{item.count}} 31 <button @click="handleAdd(index)">+</button> 32 </td> 33 <td> 34 <button @click="handleRemove(index)">移除</button> 35 </td> 36 </tr> 37 </tbody> 38 </table> 39 <div>總價:¥ {{totalPrice}}</div> 40 </template> 41 <div v-else>購物車爲空</div> 42 </div> 43 44 <script src="https://unpkg.com/vue/dist/vue.min.js"></script> 45 <script src="index.js"></script> 46 </body> 47 </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;}
練習1:在當前示例基礎上擴展商品列表,新增一項是否選中該商品的功能,總價變爲只計算選中商品的總價,同時提供一個全選的按鈕。
練習2:將商品列表list
改成一個二維數組來實現商品的分類,好比可分爲"電子產品"、"生活用品"和"果蔬",同類商品聚合在一塊兒。提示,你可能會用到兩次v-for
。