[Vue] Vuex 是什麼? 怎麼用? — Modules (4/5)

itsems
itsems_frontend
Published in
10 min readMay 3, 2020
全系列共五篇:
一、[Vue] Vuex 是什麼? 怎麼用? — State、Mutations (1/5)
二、[Vue] Vuex 是什麼? 怎麼用? — Actions (2/5)
三、[Vue] Vuex 是什麼? 怎麼用? — Getters (3/5)
四、[Vue] Vuex 是什麼? 怎麼用? — Modules (4/5)
五、[Vue] Vuex 是什麼? 怎麼用? — 統整、專案結構(5/5)
Outline:
1. Why Modules
2. Usage

Why Modules

如果專案規模比較龐大,store 裡面的 state 可能會變得越來越肥,可能會有會員資訊、訂單資訊、商品資訊、最新消息…等,所以 Vuex 允許我們使用 Modules 來解決這個問題,將 store 中各類的 state 分類管理

Usage

Module — State

原本的 state 已經超級肥:

把這些不同分類的 state 拆成物件分出去,各自的 state 也可以擁有自己的 state, action, getters…等,並在 new Vuex 的時候註冊他們:

在 component 取用 modules 的 state :

Module — Mutations

mutations 一樣是吃 state, payload 兩個參數:

const moduleUser = {
state: {
name: "emma",
age: 18
},
mutations: {
chageAge(state, payload) {
state.age++;
}
},
}

這邊的 state.age 代表 moduleUser 自己的 state

Module — Getters

Module 裡面的 Getters 會有這些參數:state, getters, rootState, rootGetters ,前面的 state, getters 一樣是 module 自己的 state 跟 getters,rootState, rootGetters 則表示回到根部 (new Vuex.Store 那一層),舉例:

app.vue:

在 template 使用 module user 的 getters,讓 getters 拿到自己的 state、另一個 module 的 state,和根部的 state

這邊直接 import 了 mapGetters 用,如果不知道 mapGetters 是什麼可以參考我的第三篇:[Vue] Vuex 是什麼? 怎麼用? — Getters (3/5)

store.js

這邊要注意一個小地方,在 moduleUser 裡面的 getters 這邊,假設如範例我要使用 rootState 這個參數,前面兩個 state, getters 也要帶上去,如果只寫 rootState 就會被當成自己的 state,他是依序判斷參數的

就是醬子,不小心透露我本人 20 歲(開玩笑的不要檢舉我)

Module — Actions

actions 在第二篇說明 actions 的時候有提到他可以帶入的參數,context 原本有 commit, dispatch, state, getters,在 modules 裡面,和 getters 一樣也有 rootState, rootGetters 可以用

區域、全域、namespaced

使用了 module 之後,module 們的 actions, mutations, getters 都是全域共用的,全域共用是什麼意思呢?意思就是如果 moduleA 和 moduleB 都有一個 actions 叫做 sayHi,那麼我在 component dispatch sayHi 的話,兩個 module 的 sayHi 都會執行,下面用範例來看看:

複習一下:
commit 拿來呼叫 mutations,dispatch 拿來呼叫 actions
actions 拿來呼叫 mutations,只有 mutations 可以更動 state

先在 store.js 中將兩個 module 新增一個一樣名字的 actions sayHi:

接著在 app.vue 的 template 中,在 mounted 的時候 dispatch sayHi 這個 actions:

mounted() {
this.$store.dispatch("sayHi");
},

就會看到 console 中的確跑出了兩句,真的兩個 module 的 actions 都執行了

為了避免這樣的狀況發生,除了把名稱都設定成不一樣的,可以在 module 裡面加上一個設定:

namespaced: true

以剛剛的兩個 Hello 來看,我們把兩個 module 都加上這個設定:

加上了 namespaced 這個設定之後,在 component 要 dispatch 這個 actions 前面就要加上 module 的名稱:

mounted() {
this.$store.dispatch("user/sayHi");
this.$store.dispatch("another/sayHi");
},

上面的 age getters 範例也是一樣的道理,我們剛剛用 mapGetters 呼叫了 moduleUser 的 getters,不用指定是哪個 module 的 getters,他就成功執行了,就是因為 getters 是全域共用的,現在 module 自己已經加上了 namespaced 這個設定,getters 也要加上才能成功使用:

...mapGetters({
GetAge: "user/GetAge",
GetRealAge: "user/GetRealAge",
GetNextAge: "user/GetNextAge"
})

這裡把剛剛 mapGetters 用法從陣列轉為物件了,因為原本陣列的用法,template 上的名字需要和 getters 一樣,現在因為 namespaced 的關係,getters 的名字前面需要加上 module 的名字,所以就改用物件把 template 的 getters 名稱指定到 module 的 getters。

在 modules 之間 actions 或 mutations 互相呼叫也一樣,但是有一個設定要加,原本在 module 加上 namespaced 之前,如果 moduleA 的 actions 要呼叫 moduleB 的 mutations,會是這樣:

因為 module 們的 actions, mutations, getters 都是全域共用的,所以我在 moduleAnother 的 actions addYear commit 呼叫 moduleUser 的 mutations addAge 直接呼叫就會呼叫得到了

如果已經忘了 actions 來這邊複習:[Vue] Vuex 是什麼? 怎麼用? — Actions (2/5)

在 template 這邊加上一個 button,:

這樣在點擊 button 的時候,上面的 age 就會跟著變動了

那麼加上 namespaced 之後要怎麼寫呢?

先把 template 上的 methods 加上 model name:

methods: {
add() {
this.$store.dispatch("another/addYear");
}
}

接下來在 store.js 中,在 moduleAnother 的 actions addYear commit 會帶上三個參數:mutations, payload, {root:true}

commit('user/addAge', 2, { root: true })

第一個參數一樣是要呼叫的別的 module 的 mutations,第二個參數則為 payload,如果不需要 payload,也可以寫 null 就好,第三個 { root: true } 表示為根部,就是從底部找的意思,開啟 namespaced 後,module 之間的 actions, getters, mutations 之間要互相呼叫都要記得加上{ root: true },如果沒有加上這句話,vuex 會當作你還是要呼叫自己的東西,就會錯了

Module — 使用 mapState, mapMutations, mapActions, mapGetters

以上述的範例來看,假設我有多個 state,使用 mapState,也在 namespaced 開啟的狀況下,會變成像是:

/app.vue

/store.js

在 component 中,如果有很多個 state 要指定,所以也可以將

...mapState({
MyName: state => state.user.name,
gender: state => state.user.gender,
addr: state => state.user.addr
}),

改寫為:

...mapState("user", {
MyName: state => state.name,
gender: state => state.gender,
addr: state => state.addr
}),

在 maptState 前面加入一個 module 名的字串,就可以指定要找的 module 名稱了。

如果 template 上的名稱跟 state 裡面的名稱一樣,改為陣列的話,會更簡短:

...mapState("user", ["name", "gender", "addr"]),

module 的 mapMutations、mapGetters、mapActions 也都是一樣的,

把剛剛的 mapGetters 再拿回來看一次:

...mapGetters({
GetAge: "user/GetAge",
GetRealAge: "user/GetRealAge",
GetNextAge: "user/GetNextAge"
})

可以改成:

...mapGetters("user", ["GetAge", "GetRealAge", "GetNextAge"])

或是再更精簡一點:

這樣在這邊的 mapState 和 mapGetters 就會直接去 user 這個 module 裡面找了。

Demo code in Github

Module 的用法差不多就說明到這邊,講了很多,下一篇再把前面的重點整理下 🏃🏃🏃

--

--

itsems
itsems_frontend

Stay Close to Anything that Makes You Glad You are Alive.