Vue.js 學習筆記 第5章 內置指令

 

本篇目錄:

5.1 基本指令css

5.2 條件渲染指令html

5.3 列表渲染指令 v-forvue

5.4 方法與事件webpack

5.5 實戰:利用計算屬性、指令等知識開發購物車web

 

回顧一下第2.2節,咱們己經介紹過指令(Directive)的概念了,Vue.js的指令是帶有特殊前綴v-的HTML特性,它綁定一個表達式,並將一些特性應用到DOM上。
其實咱們已經用到過不少Vue內置的指令,好比v-htmlv-pre,還有上一章的v-bind
本章將繼續介紹Vue.js中更多經常使用的內置指令。vue-router

5.1 基本指令

5.1.1 v-cloak

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安全

5.1.2 v-once

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在業務中也不多使用,當你須要進一步優化性能時,可能會用到。

5.2 條件渲染指令

5.2.1 v-if、v-else-of、v-else

與JavaScript的條件語句ifelseelse 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-ifv-else要緊跟v-else-ifv-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屬性。

5.2.2 v-show

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>上使用。

5.2.3 v-if與v-show的選擇

v-ifv-show具備相似的功能,不過v-if纔是真正的條件渲染,它會根據表達式適當地銷燬或重建元素及綁定的事件或子組件。
若表達式初始值爲false,則一開始元素/組件並不會渲染,只有當條件第一次變爲true時纔開始編譯。

v-show只是簡單的CSS屬性切換,不管條件真與否,都會被編譯。
相比之下,v-if更適合條件不常常改變的場景,由於它切換開銷相對較大,而v-show適用於頻繁切換條件。

5.3 列表渲染指令 v-for

5.3.1 基本用法

當須要將一個數組遍歷或枚舉一個對象循環顯示時,就會用到列表渲染指令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>

 

5.3.2 數組更新

Vue的核心是數據與視圖的雙向綁定,當咱們修改數組時,Vue會檢測到數據變化,因此用v-for渲染的視圖也會當即更新。
Vue包含了一組觀察數組變異的方法,使用它們改變數組也會觸發視圖更新:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

例如,咱們將以前一個示例的數據books添加一項:

1 app.books.push({
2     name: "《CSS解密》",
3     author: "[希] Lea Verou"
4 });

 

能夠嘗試編寫完整示例來查看效果。
使用以上方法會改變被這些方法調用的原始數組,有些方法不會改變原數組,例如:

  • filter()
  • concat()
  • slice()

它們返回的是一個新數組,在使用這些非變異方法時,能夠用新數組來替換原數組。
仍是以前展現節目的示例,咱們找出含有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);

 

5.3.3 過濾與排序

當你不想改變原數組,想經過一個數組的副原本作過濾或排序的顯示時,可使用計算屬性來返回過濾或排序後的數組。
例如:

 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中內置的limitByfilterByorderBy過濾器,統一改用計算屬性來實現。

5.4 方法與事件

5.4.1 基本用法

在第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>

 

5.4.2 修飾符

在上例使用的event.preventDefault()也能夠用Vue事件的修飾符來實現。
@綁定的事件後加小圓點.,在跟一個後綴來使用修飾符。
Vue支持如下修飾符:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once

具體用法以下:

 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還提供了一些快捷名稱。
如下是所有的別名:

  • .enter
  • .tab
  • .delete(捕獲"刪除"和"退格"鍵)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

這些按鍵修飾符也能夠組合使用,或和鼠標一塊兒使用:

  • .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的基本內容,在第7章的組件中,咱們還將介紹用v-on來綁定自定義事件。

5.5 實戰:利用計算屬性、指令等知識開發購物車

前五章內容基本涵蓋了Vue.js最核心的經常使用的知識點,掌握這些內容已經能夠上手開發一些小功能了。
本節則以Vue.js的計算屬性、內置指令、方法等內容爲基礎,完成一個在業務中具備表明性的小功能:購物車。

在開始寫代碼前,要對需求進行分析,這樣有助於咱們理清業務邏輯,儘量還原設計產品交互。

購物車須要展現一個已加入購物車的商品列表,包含商品名稱、商品單價、購買數量和操做等信息,還須要實時顯示購買的總價。
其中購買數量能夠增長或減小,每類商品還能夠從購物車中移除。
最終實現的效果如圖5-6所示:

在明確需求後,咱們就能夠開始編程了。
由於業務代碼較多,此次咱們將HTML、CSS、JavaScript分離爲3個文件,便於閱讀和維護:

  • index.html (引入資源及模板)
  • index.js (Vue實例及業務代碼)
  • index.css (樣式)

如今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.jsindex.js文件寫在<body>的最底部,若是寫在<head>裏,Vue實例將沒法建立,由於此時DOM尚未被解析完成,除非經過異步或在事件DOMContentLoaded(IE是onreadystatechange)觸發時再建立Vue實例,這有點像jQuery的$(document).ready()方法。

本例須要用到Vue.js的computedmethods等選項,在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.jscomputed選項內寫入:

 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>內把數組listv-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>用於增/減購買數量,分別綁定了兩個方法handleReducehandleAdd,參數都是當前商品在數組list中的索引。
不少時候,一個元素上會同時使用多個特性(尤爲是在組件中使用props傳遞數據時),寫在一行代碼較長,不便閱讀,因此建議特性過多時,將每一個特性都單獨寫爲一行,好比第一個<button>中使用了v-bindv-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

相關文章
相關標籤/搜索