0%

[Vue] Vuex 資料狀態管理學習筆記之實作起手式

上週的 Vuex 筆記中介紹完了基本概念,這週的筆記就著重在實作上囉!
不過由於實作的部分篇幅過長,所以會分成比較多篇來發布。

新增一個 store 管理網站資料狀態

  1. 下載 Vuex,並到 main.js 啟用 Vuex
  2. 在 src 新增 store 資料夾,並在裡面新增 index.js
  3. 在 index.js 中也要引入 Vue 及 Vuex (參照:官網安裝說明
  4. 接著使用以下語法:
1
export default new Vuex.Store({});
  1. 回到 main.js,把 store/index.js import 進來,下方的 new Vue 中也要引入 store (寫在 router 的下一行)

將 vue-loading-overlay 的讀取效果存入 store

  1. 在 store/index.js 的物件中,新增 state 物件,並在裡面加入 isLoading 特性,先預設值為 false
1
2
3
4
5
export default new Vuex.Store({
state: {
isLoading: true,
}
});
  1. 此時,其他元件中就不需要有 isLoading 這個資料,而有用到 isLoading 的程式碼也要先註解起來

  2. 如果外層元件跟內元件都要使用讀取效果時,應該要統一把效果加在外層。此時外層元件必須使用 computed 來操作 store 中的資料,而且 isLoading 這個變數要變成 function 的形式,之所以 function 要取名為 isLoading 是因為要對應 vue-loading-overlay 的模板 <loading :active.sync="isLoading"></loading>

1
2
3
4
5
6
// 要使用讀取效果的外層元件
computed: {
isLoading() {
return this.$store.state.isLoading
}
}
  1. 接著來到內元件,內元件就不需要 loading 模板了,但依然需要在 methods 中切換 store 的 isLoading 值。
1
2
3
4
5
6
7
8
9
10
11
12
13
getProducts() {
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products`;
const vm = this;
// 開啟 loading
vm.$store.state.isLoading = true;
this.$http.get(api).then((response) => {
console.log(response.data);
// 關閉 loading
vm.$store.state.isLoading = false;
// 存到陣列裡
vm.products = response.data.products;
});
}

使用 Actions 及 Mutations 變更資料狀態

上一段改變資料狀態的方法其實並不正確,透過 Vue dev tool 可以發現資料狀態並沒有響應式,因此這一段就要來介紹如何正確地改變資料狀態。(參照:官方文件 Action 介紹

  1. 在 store 新增 actions 物件及 mutations 物件。

  2. 在 actions 中新增 updateLoading() 並傳入兩個參數,分別是 contextstatus。第一個參數是固定的,第二個參數則是開發者自訂的,由外部傳進來。(參照:官網 API 參考

1
2
3
4
actions: {
// status 原始的參數名稱為 payload
updateLoading(context, status){}
}
  1. Mutations 中的函式命名方式建議採用常數命名,也就是所有字母都用大寫。Mutations 要傳入的參數也有兩個,第一個固定都是 state(代表資料狀態),另一個是 payload(在此我們自定義名稱為 status)。
1
2
3
4
mutations: {
// 第二個參數為 updateLoading 第二個參數所代表的值
LOADING(state, status) {}
}
  1. 由於我們已經希望達成的任務就是修改 state 資料狀態,所以可以先在 mutations 裡寫好 state 變更後的結果。
1
2
3
4
5
6
mutations: {
// 第二個參數為 updateLoading 第二個參數所代表的值
LOADING(state, status) {
state.isLoading = status;
}
}
  1. 接著再到 actions 來觸發 mutations 的變化。使用 contextcommit 方法,並帶入對應的 mutations 名稱與要傳到該 mutations 的參數(status)。
1
2
3
4
5
actions: {
updateLoading(context, status){
context.commit('LOADING', status);
}
}
  1. 回到內元件,原本在 methods 直接操作 store 資料的程式碼要刪掉,改成使用 dispatch() 來觸發 actions 中的函式。
1
2
3
4
5
6
// 開啟讀取效果
// 第二個參數帶入新的值
vm.$store.dispatch('updateLoading', true);

// 關閉讀取效果
vm.$store.dispatch('updateLoading', false);
  1. 在 Vue 的開發者工具中,可以看到 Vuex 管理的資料,以及不同時間點資料個別的狀態。

store 中 commit()dispatch() 的區別

這兩種方法都用在 actions 的函式中,然而,commit() 用來觸發 mutations 中的資料函式;dispatch() 則是用來觸發其他 actions 中的函式,而且可以帶入載荷(payload,也就是參數值)。

使用嚴謹模式可以更正確使用 Vuex

state 物件上面新增 strict: true, 這行程式碼,當我們寫出不合乎 Vuex 規範的程式碼時,console 就會跳錯,例如不應該在 actions 操作資料狀態等等。

但是,使用嚴謹模式將會有一個很不便利的地方,那就是在採用雙向綁定時不能直接用 v-model,而是必須多繞點彎,在 computed 設定 getter 跟 setter,詳細做法可參考官方文件:表单处理

使用 Actions 取得遠端資料

  1. 起手式是在 store 中 import axios:import axios from 'axios'
  2. 先在 state 中定義資料結構,用來儲存遠端取回來的資料。
1
2
3
4
5
state: {
...
products: [],
categories: []
}
  1. 把原本元件中負責 AJAX 的函式貼到 actions 裡面,記得帶入 context 參數,並對程式碼做修正。例如,把寫在 updateLodaing() 中的 commit() 移到這裡面(status 改成 truefalse),代替原本的 dispatch();AJAX 的方法要從 this.$http 改為 axios;還要記得把儲存遠端資料的程式碼移到 mutations 裡面。
1
2
3
4
5
6
7
8
9
getProducts(context) {
const url = `${process.env.VUE_APP_APIPATH}/api/${process.env.VUE_APP_CUSTOMPATH}/products/all`;
context.commit('LOADING', true);
axios.get(url).then((response) => {
context.commit('PRODUCTS', response.data.products);
context.commit('CATEGORIES', response.data.products);
context.commit('LOADING', false);
});
},
  1. 在 mutations 新增函式來儲存遠端取回來的資料。
1
2
3
4
5
6
7
8
9
10
PRODUCTS(state, payload) {
state.products = payload;
},
CATEGORIES(state, payload) {
const categories = new Set();
payload.forEach(item => {
categories.add(item.category);
});
state.categories = Array.from(categories);
}
  1. 接著要把 actions 中的函式掛載到元件中。到 methods 中,在 actions 對應的函式中呼叫 actions。
1
2
3
getProducts() {
this.$store.dispatch('getProducts');
}
  1. 最後則是要把資料渲染到畫面上。在 computed 中新增函式來達成,函式的名稱依照資料特性名稱來取。
1
2
3
4
5
6
categories() {
return this.$store.state.categories;
},
products() {
return this.$store.state.products;
}