本篇只是本人對 module 的學習總結和理解以及一些避免坑的意見,不必定能夠用在你的項目當中,即便你要使用,建議你先參考官方對比下先後文。
另外,module 是基於 vuex 即 store 狀態的模塊化管理方案,因此本篇是針對有過 store 使用經驗的同窗的一篇僅供參考的我的總結,若是你還不會 store 你得抓緊了! 或者你能夠參考 大宏說
老師的《Vuex白話教程第六講:Vuex的管理員Module(實戰篇)》javascript
模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象:css
const moduleA = {
state: { count:10 },
getters:{
filterCount(state){
return state.count * 2; // 20;
}
},
mutations:{
add(state){
state.count * 2; // 20;
}
},
}
複製代碼
action則是經過context.state
暴露出來:html
actions:{
incrementIfOddOnRootSum(context){
context.state.count // 10
}
}
複製代碼
action 能夠經過rootState
獲取到根節點的狀態:vue
actions:{
incrementIfOddOnRootSum( { state, rootState } ){
rootState.xx // 根節點的xx
}
}
複製代碼
getter 接受根節點狀態是經過第三個參數暴露出來:java
getters:{
sumWithRootCount(state, getters, rootState){
//state 是模塊內部的狀態
// getters 模塊內部的其餘getter
// rootState 是全局的狀態
}
}
複製代碼
若是模塊不使用命名空間的話,默認狀況下模塊內部的 getter, action 和 mutation是註冊在全局全局命名空間的,這樣的壞處是:web
store:{
state:{
count:18,
},
mutations:{
setCount(state){
state.count / 2;
console.log(state.count) // 9
}
},
modules:{
a:moduleA
}
}
moduleA:{
state:{
count:10,
},
mutations:{
setCount(state){
state.count * 2;
console.log(state.count)//20
}
}
}
複製代碼
在提交 moduleA 的 mutation
時:vuex
this.$store.commit('setCount');
// 猜猜會打印啥?
// 9 和 20 這是由於前面所說的模塊內部的getter,action和mutation是註冊在全局全局命名空間的。
//因此上面的例子中全局命名空間裏有2個名爲setCount的mutation,而後他們都被觸發了。
複製代碼
想要讓模塊內部的 getter, mutation , action只做用域當前局部模塊內的話能夠給模塊添加namespaced
屬性:api
modules:{
moduleA:{
namespaced:true,
state:{...}, //state仍是僅做用於當前模塊內部
getter:{
isAdmin(){...}
},
mutations:{
login(){...}
},
actions:{
getToken(){...}
}
}
}
複製代碼
當開啓命名空間的模塊被註冊後它的全部 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名,因此觸發路徑也有所改變:bash
store.getters['moduleA/isAdmin']; // 命名空間模塊內部的getter
store.dispatch('moduleA/getToken'); // 命名空間模塊內部的action
store.commit('moduleA/login'); // 命名空間模塊內部的mutation
複製代碼
文檔裏也有說模塊內是還能夠嵌套模塊的大概意思就是:app
modules:{
moduleA:{
state:{...},
mutations:{...},
// 嵌套模塊
modules:{
mypage:{
state:{...},
getters:{
profile(state){}//由於嵌套模塊沒有本身命名空間,因此就自動繼承了父命名空間,因此就能夠這樣觸發這個getter:store.getters['moduleA/profile'];
}
},
// 進一步嵌套命名空間
posts:{
namespaced:true,//開啓命名空間
state:{...},
getters:{
popular(){...}//前面咱們說過,開啓命名空間的模塊它全部的getter、action、mutation都會自動根據模塊的路徑調整命名 -> store.getters['moduleA/posts/popular']
}
}
}
}
}
複製代碼
接着上面繼承命名空間的例子:
modules:{
moduleA:{
state:{...},
mutations:{...},
// 嵌套模塊
modules:{
mypage:{
state:{...},
getters:{
profile(){...}
}
}
}
}
}
複製代碼
若是我如今要觸發profile
這個getter我能夠這樣:
store.getters['moduleA/profile'];
複製代碼
由於即便是嵌套的模塊但mypage沒有本身的命名空間因此繼承了父命名空間,因此這樣觸發看上去沒有問題。
問題來了⚠️
若是父命名空間內也有一個名爲 profile
的getter:
modules:{
moduleA:{
state:{...},
getters:{
profile(state){...}
}
mutations:{...},
// 嵌套模塊
modules:{
mypage:{
state:{...},
getters:{
profile(){...}
}
}
}
}
}
複製代碼
這個時候若是再執行:
store.getters['moduleA/profile'];
複製代碼
會是什麼結果呢?你們能夠本身動手試一試,加深一下印象。
若是你但願使用全局 state 和 getter,rootState 和 rootGetters 會做爲第三和第四參數傳入 getter,也會經過 context
對象的屬性傳入 action。
modules: {
foo: {
namespaced: true,
getters:{
someGetter(state, getters, rootState, rootGettrers){
// state 是當前模塊內部是狀態
// getters 是當前模塊內部的 getters
// rootState 是全局下的狀態
// rootGettrers 是全局下的 gettrers
}
},
actions:{
someAction( { getters, rootGetters}){
// getters 當前模塊內部的 getters,
// rootGettrers 是全局下的 gettrers
}
}
}
}
複製代碼
若須要在全局命名空間內分發 action 或提交 mutation,將 { root: true }
做爲第三參數傳給 dispatch 或 commit 便可:
action:{
someAction( { dispatch, commit}){
dispatch('someOtherAction');// 分發當前模塊內名爲someOtherAction的action
dispatch('someOtherAction', null, { root: true })// 分發全局名爲someOtherAction的action
commit('someMutation') // 提交當前模塊內名爲someMutation的mutation
commit('someMutation', null, { root: true }) // 提交全局名爲someMutation的mutation
}
}
複製代碼
是的訪問全局只須要提供
rootState
和rootGetters
參數就好,而分發action或提交mutation只須要將{ root: true }
做爲第三個參數就好。
若業務須要在帶命名空間的模塊中註冊全局 action,你可添加 root: true
,並將這個 action 的定義放在函數 handler 中。例如:
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
複製代碼
官方給了兩種方案,這裏我使用命名空間輔助函數的寫法,另外一種寫法有興趣的同窗能夠去參考一下
這個例子官方其實已經給的很簡潔直觀了因此咱們直接看:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module`上下文中 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module`上下文中 中查找
...mapActions([
'foo',
'bar'
])
}
}
複製代碼
這個例子我有一個疑問:若是mapState
和mapActions
是基於'some/nested/module'
上下文的話,那若是我這個組件內還須要使用其餘命名空間的模塊該怎麼辦呢?有的同窗可能會說:
const { mapState, mapActions } = createNamespacedHelpers('otherSome/nested/module')
複製代碼
再定義一個上下文不就行了嗎?但兩個上下文返回的都是一樣的mapState
和mapActions
,我在使用mapActions
時,你怎麼知道我是在那個上下文中查找呢?
…
後來想了想我這個疑問是否成立?由於我以爲一個模塊store
應該始終是效力於一個功能組件的。可是不保證沒有極端的狀況出現,若是真有這種需求的話,該怎麼實現?有經驗的同窗能夠教我一下。
若是有業務需求須要咱們動態註冊模塊,咱們可使用 store.registerModule
方法註冊模塊:
// 註冊模塊 `myModule`
store.registerModule('myModule', {
// ...
})
// 註冊嵌套模塊 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})
複製代碼
動態註冊的前提是你的Store已經被你建立了。
以後就能夠經過store.state.myModule
和store.state.nested.myModule
訪問模塊的狀態。
以後不須要模塊時可使用store.unregisterModule(moduleName)
來動態卸載模塊來保證性能
注意:不能使用此方法卸載靜態模塊(即建立 store 時聲明的模塊)
模塊重用,官方給的場景是:
一、建立多個store
,他們共用同一個模塊
二、在一個store
中屢次註冊同一個模塊
說的可能比較抽象,咱們來一個簡單的例子:
moduleA.js
const moduleA = {
state:{
count:10
},
mutations:{
changeCount(state){
state.count = 20;
}
}
}
export default moduleA;
複製代碼
store.js
const store = new Vuex.Store({
state:{...}
mutations:{...},
modules:{
a:moduleA,
b:moduleA,
c:moduleA
}
})
複製代碼
此時modules
對象裏的a
b
c
都引用自moduleA
模塊;
咱們再來建3個組件,而後分別引用a
b
c
這3個模塊的實例:
test1.vue
<template>
<div>
{{count}}
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapState } = createNamespacedHelpers('a')
export default {
computed: {
...mapState({
name:state => state.count
})
}
}
</script>
複製代碼
test2.vue
<template>
<div>
{{count}}
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapState } = createNamespacedHelpers('b')
export default {
computed: {
...mapState({
name:state => state.count
})
}
}
</script>
複製代碼
test3.vue
<template>
<div>
{{count}}
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapState } = createNamespacedHelpers('c')
export default {
computed: {
...mapState({
name:state => state.count
})
}
}
</script>
複製代碼
3個組件的count都等於10,由於modulea
b
c
都引用自moduleA
模塊;
此時,若是咱們在test1
組件裏提交moduleA的mutation
:
test1.vue
<template>
<div>
{{count}}
<input type="button" value="click" @click="changeCount">
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex'
const { mapState,mapMutations } = createNamespacedHelpers('a')
export default {
methods:{
...mapMutations([changeCount])
}
computed: {
...mapState({
name:state => state.count
})
}
}
</script>
複製代碼
此時,只要咱們一提交changeCount,test1
test2
test3
組件裏的count都會被改成20;
緣由:
當一個模塊被定義,模塊可能被用來建立多個實例,這時若是模塊還是一個純粹的對象,則全部實例將共享引用同一個數據對象!這就是模塊間數據互相污染的問題。
爲了解決互相污染咱們可使用一個函數聲明來返回模塊的狀態:
const MyReusableModule = {
state () {
return {
count: 10
}
},
// mutation, action 和 getter 等等...
}
複製代碼
經過爲 state 聲明一個初始數據對象的函數,且每次建立一個新實例後,咱們可以調用 state 函數,從而返回初始數據的一個全新副本數據對象。
此番借鑑Vue 組件內的 data
大體意思就是讓state以函數聲明式返回狀態,這樣無論模塊被實例化多少次,每次實例化時模塊內部的state
都會是一個全新的函數返回。
給某些同窗一些建議:作筆記、總結這種東西必定是要你本身先學習一遍,而後理解事後的記錄,並不是是把人家文檔的東西循序漸進放到你的筆記當中,這樣作的意義何在呢?騙點擊的沙雕網友咱們就不評價了,並且人家文檔的東西始終是最新的,並且也有持續更新。複製 - 粘貼 - 發佈的這類沙雕網友拜託大家不要浪費你們的時間了。淨化學習環境從我作起!
固然一篇總結老是避免不了會用到原文檔的一些例子。