上週的 Vuex 筆記中介紹完了基本概念,這週的筆記就著重在實作上囉!
不過由於實作的部分篇幅過長,所以會分成比較多篇來發布。
新增一個 store 管理網站資料狀態
- 下載 Vuex,並到 main.js 啟用 Vuex
- 在 src 新增 store 資料夾,並在裡面新增 index.js
- 在 index.js 中也要引入 Vue 及 Vuex (參照:官網安裝說明)
- 接著使用以下語法:
1
| export default new Vuex.Store({});
|
- 回到 main.js,把 store/index.js import 進來,下方的 new Vue 中也要引入 store (寫在 router 的下一行)
將 vue-loading-overlay 的讀取效果存入 store
- 在 store/index.js 的物件中,新增 state 物件,並在裡面加入
isLoading
特性,先預設值為 false
1 2 3 4 5
| export default new Vuex.Store({ state: { isLoading: true, } });
|
此時,其他元件中就不需要有 isLoading
這個資料,而有用到 isLoading
的程式碼也要先註解起來
如果外層元件跟內元件都要使用讀取效果時,應該要統一把效果加在外層。此時外層元件必須使用 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 } }
|
- 接著來到內元件,內元件就不需要 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; vm.$store.state.isLoading = true; this.$http.get(api).then((response) => { console.log(response.data); vm.$store.state.isLoading = false; vm.products = response.data.products; }); }
|
使用 Actions 及 Mutations 變更資料狀態
上一段改變資料狀態的方法其實並不正確,透過 Vue dev tool 可以發現資料狀態並沒有響應式,因此這一段就要來介紹如何正確地改變資料狀態。(參照:官方文件 Action 介紹)
在 store 新增 actions 物件及 mutations 物件。
在 actions 中新增 updateLoading()
並傳入兩個參數,分別是 context
及 status
。第一個參數是固定的,第二個參數則是開發者自訂的,由外部傳進來。(參照:官網 API 參考)
1 2 3 4
| actions: { updateLoading(context, status){} }
|
- Mutations 中的函式命名方式建議採用常數命名,也就是所有字母都用大寫。Mutations 要傳入的參數也有兩個,第一個固定都是
state
(代表資料狀態),另一個是 payload
(在此我們自定義名稱為 status
)。
1 2 3 4
| mutations: { LOADING(state, status) {} }
|
- 由於我們已經希望達成的任務就是修改
state
資料狀態,所以可以先在 mutations 裡寫好 state
變更後的結果。
1 2 3 4 5 6
| mutations: { LOADING(state, status) { state.isLoading = status; } }
|
- 接著再到 actions 來觸發 mutations 的變化。使用
context
的 commit
方法,並帶入對應的 mutations
名稱與要傳到該 mutations
的參數(status
)。
1 2 3 4 5
| actions: { updateLoading(context, status){ context.commit('LOADING', status); } }
|
- 回到內元件,原本在
methods
直接操作 store 資料的程式碼要刪掉,改成使用 dispatch()
來觸發 actions
中的函式。
1 2 3 4 5 6
|
vm.$store.dispatch('updateLoading', true);
vm.$store.dispatch('updateLoading', false);
|
- 在 Vue 的開發者工具中,可以看到 Vuex 管理的資料,以及不同時間點資料個別的狀態。
store 中 commit()
與 dispatch()
的區別
這兩種方法都用在 actions
的函式中,然而,commit()
用來觸發 mutations
中的資料函式;dispatch()
則是用來觸發其他 actions
中的函式,而且可以帶入載荷(payload,也就是參數值)。
使用嚴謹模式可以更正確使用 Vuex
在 state
物件上面新增 strict: true,
這行程式碼,當我們寫出不合乎 Vuex 規範的程式碼時,console 就會跳錯,例如不應該在 actions 操作資料狀態等等。
但是,使用嚴謹模式將會有一個很不便利的地方,那就是在採用雙向綁定時不能直接用 v-model
,而是必須多繞點彎,在 computed
設定 getter 跟 setter,詳細做法可參考官方文件:表单处理。
使用 Actions 取得遠端資料
- 起手式是在 store 中 import axios:
import axios from 'axios'
。
- 先在
state
中定義資料結構,用來儲存遠端取回來的資料。
1 2 3 4 5
| state: { ... products: [], categories: [] }
|
- 把原本元件中負責 AJAX 的函式貼到
actions
裡面,記得帶入 context
參數,並對程式碼做修正。例如,把寫在 updateLodaing()
中的 commit()
移到這裡面(status
改成 true
或 false
),代替原本的 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); }); },
|
- 在 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); }
|
- 接著要把 actions 中的函式掛載到元件中。到
methods
中,在 actions 對應的函式中呼叫 actions。
1 2 3
| getProducts() { this.$store.dispatch('getProducts'); }
|
- 最後則是要把資料渲染到畫面上。在
computed
中新增函式來達成,函式的名稱依照資料特性名稱來取。
1 2 3 4 5 6
| categories() { return this.$store.state.categories; }, products() { return this.$store.state.products; }
|