動態註冊Vuex namespaced module

本文使用版本

日期:2020/ 7/ 3
Vue: ^2.6
Vuex: ^3.3

前情提要

最近公司在開發的一個專案在開發完核心功能之後,
上頭似乎對這個專案有了些想法,開始提出進一步的需求。

也因為逐漸增加的需求,從本來不需要Vuex、到引入Vuex;
再到Vuex單一個store已經好幾百行;
(實現各個需求的state, mutations, actions都放在一個檔案(store)裡面)

再到發現有store可以被重複利用,
於是使用registerModule做動態註冊,
還用namespace機制存取剛剛註冊的module

今天正是想紀錄一下這個有趣的機制
還有workaround🤭

需求長什麼樣呢?

比如說Facebook的訊息功能好了,
每一個群組都是一個對話框,對話框裡面都需要儲存、顯示傳過的訊息,
而這個訊息就可以寫成一個MessageStore重複利用

MessageStore

最基本的程式就像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 模組都是宣告成純物件
const MessageStore = {
namespaced: true,

// 因為這個 MessageStore 會被重複使用,
// 透過這種寫法才能讓各個 MessageStore instance 的 state 互相獨立
// https://vuex.vuejs.org/zh/guide/modules.html#%E6%A8%A1%E5%9D%97%E9%87%8D%E7%94%A8
state: () => ({
messages: []
}),

getters: {
messagesCount (state) {
return state.messages.length;
},
},

// 必須搭配 namespaced:true 這個設定,
// 否則mutations, actions, getters 都會被註冊在全域
mutations: {
addMessage(state, { sent_by, content }) {
state.messages.push({
sent_by,
content,
id: (new Date).getTime()
});
}
}
};

Root Store

接著我們還需要一個 Root Store 可以給我們存放群組的資訊,
以及動態註冊module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 用 root store 來管理群組
// 在動態加入的同時,順便動態註冊MessageStore
const store = new Vuex.Store({
state: {
groups: [],
added: 0,
activeGroupId: null
},
mutations: {
addGroup (state, group) {
state.groups.push(group);
state.added++;
},
activateGroup (state, { id }) {
state.activeGroupId = id;
},
// 這種動態註冊 module 的 function 也可以寫在 vue component,
// 寫在這邊單純是想給需要的人示範在mutation裡面做...
initMessageStore (state, { id }) {
// 這邊的 this 指向 root store
const exists = this.hasModule(['groupMessages', id]);
if (! exists) {
this.registerModule(['groupMessages', id], MessageStore);
}
},
},
getters: {
// 用現在開啟的groupId 找到動態module的state
// 注意,從 nested module 取出 state、getters 的寫法各有不同~
curMessages(state) {
return state.activeGroupId
? state.groupMessages[state.activeGroupId].messages
: [];
},
curMessagesCount(state, getters) {
return state.activeGroupId
? getters[`groupMessages/${state.activeGroupId}/messagesCount`]
: 0;
}
},
modules: {
// 如果要直接宣告nested module,要確保每一層module都存在,在這邊我就直接宣告了
// 可以拿掉試試看,會出現『parent is undefined』的訊息唷
groupMessages: {

// 你可能會覺得 MessageStore 那邊就有 namespaced:true 的設定了,這邊是不是多餘的?
// 其實不是
// 如果拿掉,會出現『[vuex] unknown mutation type: groupMessages/1/addMessage』的錯誤喔
namespaced: true

}
},
});

Vue

接著呢,就是把 Root store 註冊進 vue instance,
然後完成一些額外的東西就行拉

直接看看實際上的code吧!