0%

[Vue] Vuex 資料狀態管理學習筆記之功能實作篇

學習六角學院「Vue 出一個電商網站」課程時,課程後半段是跟著老師的示範,實作出電商網站的完整功能。

這篇筆記便是紀錄了當時實作的部分功能:Navbar 中的購物車列表、商品頁側欄的商品分類。

製作 Navbar 上的購物車

  1. 步驟類似上一篇最後一段「使用 Actions 取得遠端資料」。首先將「取得購物車資料」(getCart()) 及「刪除購物車品項」(removeCart()) 的 AJAX 寫到 store 中的 actions 中,購物車資料結構也從元件 data 搬到 store 中的 state

  2. 整理程式碼,例如把 AJAX 的 http 方法改成 axios,帶入 context 參數,以及刪除購物車品項後重新取得購物車清單的程式碼,要改成以下這段:

1
context.dispatch('getCart');
  1. 回到 App.vue,原本的 methods 程式碼改成呼叫 store 中的 actions,且 computed 也要呼叫 state 中的資料。
1
2
3
4
5
6
getCart() {
this.$store.dispatch('getCart', );
},
removeCart(id) {
this.$store.dispatch('removeCart', id);
}
1
2
3
4
5
6
computed: {
...
cart() {
return this.$store.state.cart;
}
},
  1. 把「加入購物車」的功能加進來。到 Home.vue,複製 addtoCart() 的程式碼,貼到 actions 中。一樣要整理程式碼,用 dispatch() 呼叫 getCart()

  2. addtoCart() 因為會從 Home.vue 傳遞兩個參數到 actions 的關係,所以就帶出一個問題:actions 中的函式一次只能從外部接收一個參數。解決方式就是在 Home.vue 的 addtoCart() 中用物件把兩個參數包起來,而 actions 中的 addtoCart() 傳入的參數也用物件包起來。

1
2
3
4
5
6
7
8
9
// Home.vue
addtoCart(id, qty = 1) {
this.$store.dispatch('addtoCart', {id, qty});
}

// store 的 actions
addtoCart(context, {id, qty}) {
...
}

製作側欄商品分類的功能

前置作業

  1. 已經在 store 處理好商品分類有哪些。
1
2
3
4
5
6
7
8
9
// mutations
CATEGORIES(state, payload) {
const categories = new Set();
payload.forEach(item => {
categories.add(item.category);
});
state.categories = Array.from(categories);
// 寫入 state 裡
},
  1. 在元件中,已經用 computed 從 store 取回商品資料與分類資料。

  2. 元件模板中有搜尋欄,並綁定 v-model 以取得搜尋欄中的文字。

側欄分類連結

  1. 商品分類用 v-for 從取回來的商品資料疊代。
  2. 點擊該分類時,搜尋欄文字要等於這個分類。
  3. 當搜尋欄文字等於這個分類時,class 為啟用狀態。
  4. 按下「全部顯示」這個分類時,搜尋文字清空,且當搜尋文字為空時,class 為啟用狀態。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="list-group sticky-top">
<a class="list-group-item list-group-item-action"
href="#"
@click.prevent="searchText = item"
:class="{ active: item === searchText }"
v-for="item in categories"
:key="item">
<i class="fa fa-street-view" aria-hidden="true"></i>
{{ item }}
</a>
<a href="#" class="list-group-item list-group-item-action"
@click.prevent="searchText = ''"
:class="{ active: searchText === '' }">
全部顯示
</a>
</div>

商品過濾的邏輯

在 computed 中新增 filterData(),在這裏比對商品資料的「分類」與 data 定義的「搜尋欄文字」是否符合,並 return 符合搜尋欄文字的商品。

1
2
3
4
5
6
7
8
9
10
11
12
13
filterData() {
const vm = this;
if (vm.searchText) {
// vm.products 指元件中從 store 取得資料的 computed
return vm.products.filter(item => {
const data = item.category
.toLowerCase()
.includes(vm.searchText.toLowerCase());
return data; // data 為過濾條件
});
}
return this.products;
},

子畫面呈現過濾後的商品列表

商品卡片最外層用 .col-md-4 包住,並且在這一層用 v-forcomputed filterData() 中疊代出過濾後的每一項商品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div
class="col-md-4 mb-4"
v-for="item in filterData"
:key="item.id">
<div class="card border-0 box-shadow text-center h-100">
<img class="card-img-top" :src="item.image" alt="Card image cap" />
<div class="card-body">
<h4 class="card-title">{{ item.title }}</h4>
<p class="card-text text-left">{{ item.content }}</p>
</div>
<div class="card-footer border-top-0 bg-white">
<button class="btn btn-outline-secondary btn-block btn-sm"
@click="addtoCart(item.id)">
<i class="fa fa-cart-plus" aria-hidden="true"></i>
加到購物車
</button>
</div>
</div>
</div>