首先先回顧一下上週提到的 Store 中有這些東西:
還記得上週取得資料的時候,我們直接使用 $store.state
在 computed 中直接抓資料嗎?
當資料需要被處理過的話,當然也可以從 computed 中進行處理,例如以上週的 TodoList 例子來說,假設我需要計算有幾筆是已完成的,就可以這樣寫:
computed: {
countTodoDone() {
let todolist = this.$store.state.todolist;
return todolist.filter((item) => {
return item.done === true;
});
},
},
那 getters 要幹嘛xD?
當有多個地方需要用到相同的計算結果時,getters 就會現身~
而他的寫法就跟 computed 一樣:
getters: {
countTodoDone() {
let todolist = this.$store.state.todolist;
return todolist.filter((item) => {
return item.done === true;
});
},
},
準備好 getters 之後,在元件中就能使用 mapGetters()
的方式來取得計算結果囉~
//元件中先 import 它
import { mapGetters } from "vuex";
export default {
...,
computed: {
...mapGetters(["countTodoDone"]),
},
};
如果有多個 getters 要取得,就直接加在陣列中就好:
...mapGetters(["countTodoDone", "AAA", "BBB"]),
在大型專案中,不可能只存在一個元件,當有多個元件都要存取 Store,這時候就會容易發生衝突的狀況
例如取了一樣的資料名稱或是方法名稱等等
這時候就可以將 Store 進行模組化 modules ,分割成多個模組來解決這個困境
由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常複雜時,store 對象就有可能變得相當臃腫。
為了解決以上問題,Vuex允許我們將store分割成模塊(module)。
而分割出來的每一個模組都各自包含自己的:
官網的模組化範例是這麼寫的:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
可以看到範例中 moduleA 和 moduleB 各自有自己的內容,最後放進 Store 的 modules
不過我的觀念是不會把模組和 Store 寫在一起,因為這樣全擠在一坨很噁心而且不好維護~
那要怎麼寫呢?
我們拿昨天的 TodoList 來改改看吧~
首先,在 /store 資料夾裡面新增一個檔案 - todolist.js
src
└── store
├── index.js
└── todolist.js //新增這個檔案
模組中該有的基本內容都加上去:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default {
state: { },
actions: { },
mutations: { },
getters: { }
};
注意這邊匯出(export)的不是 Store 喔!!
有些菜鳥沒注意到 (就是我)
把跟 TodoList 相關的內容全部移植過去:
export default {
state: {
todolist: [
{ id: 1, todo: "交女朋友", done: false },
{ id: 2, todo: "財務自由", done: false },
{ id: 3, todo: "當碼農", done: true },
],
},
actions: {
todoDone(context, { id, done }) {
context.commit('TODOLIST', { id, done });
},
},
mutations: {
TODOLIST(state, { id, done }) {
state.todolist.find((item) => {
return item.id === id
}).done = done;
},
},
getters: {
countTodoDone() {
let todolist = this.$store.state.todolist;
return todolist.filter((item) => {
return item.done === true;
});
},
}
};
在 /store/index.js 主檔中引用 todolist.js:
import TodoListModules from './todolist'
並且加進 modules:
export default new Vuex.Store({
...,
modules: {
RoomsModules,
TodoListModules,
},
});
開啟 App.vue 在取得 Store 資料的部分要稍做修改
computed: {
myTodo() {
return this.$store.state.TodoListModules.todolist;
},
},
到這邊暫停一下,這裡模組化的內容中
state 是註冊在 TodoListModules 命名空間中
而其他的 actions & mutations & getters 則是註冊在全局命名空間
所以會發現就算 todoDone() 不加上模組也能呼叫:
methods: {
todoDone(id, done) {
this.$store.dispatch("todoDone", { id, done });
},
},
官方是這麼說明的:
默認情況下,模塊內部的action、mutation和getter是註冊在全局命名空間的——這樣使得多個模塊能夠對同一mutation或action作出響應。
如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加namespaced: true的方式使其成為帶命名空間的模塊。
當模塊被註冊後,它的所有getter、action及mutation都會自動根據模塊註冊的路徑調整命名。
意思就是如果想讓模組完整度高一點,可以加上 namespaced: true
這個屬性,我們來加看看吧~
// /store/todolist.js
export default {
namespaced: true,
...,
};
這時候點清單會發現console跳錯囉!!
///[vuex] unknown action type: todoDone
回到 todoDone() 來加上模組,就又正常囉~
methods: {
todoDone(id, done) {
this.$store.dispatch("TodoListModules/todoDone", { id, done });
},
},
到這就大功告成~ 完美移植TodoList模組化~
可以看一下畫面一樣可以動 哈哈xD
目前我研究到這而已~
已經可以把很多現有的專案改良了!!
其他功能,之後如果有再深入探討的話再來發一篇吧~