問題描述
使用vuex的store的過程中,發現了一些不是很優雅的地方:
- store層module太多,找state、getter、mutation、action對應的module比較慢。
- 元件裡面mapGetters、mapActions、mapMutations過多,分不清getter、action、mutation屬於哪個module,比較混亂。
- 沒有使用mutation type 和action type的列舉常量來約束action type和mutation type取值,字串方式容易出錯。(如上圖)
解決方案
針對這3個問題,制定了3條重構方案。
1. module聚類分層
按照聚類分層思想,當業務複雜後,需要通過一定的聚類特徵對扁平化的結構進行分層。
這裡按照資料的用途分了page、components、domain、other這四類,page儲存頁面元件的資料,components儲存基礎元件的資料,domain儲存實體的資料,other儲存其他全域性資料。
之前的modules
之後的modules
目前還沒有儲存實體資料的module,所以暫時為空2.module新增namespace
store劃分module是因為不同的資料有不同的歸屬。
如果想要每個module都能響應全域性action的話,不需要加namespace,但是我們並沒有沒有一個action對應多個module的action handler的情況。反而因為沒有加namespace,導致元件裡的多個module的getter、action、mutation都扁平的堆在一起,結構混亂、不清晰。
...mapMutations([
changeisIceTree: 'changeisIceTree',
changeIceTreeStatus: 'changeIceTreeStatus',
showToast: 'showToast',
changeremainingfronzeTime: 'changeremainingfronzeTime',
decreaseremainingfronzeTime: 'decreaseremainingfronzeTime',
changeiceTreeFadeout: 'changeiceTreeFadeout',
changeiceTreeFadeIn: 'changeiceTreeFadeIn',
changefrozenTimes: 'changefrozenTimes',
changetreecurTime: 'changetreecurTime',
changequickTreeMedal:'changequickTreeMedal',
changequickHonorMedal:"changequickHonorMedal",
upDatePopUpOptionStatus: 'upDatePopUpOptionStatus'
}),
複製程式碼
一堆的mutation讓人迷惑,結構很不清晰,哪個mutation是哪個module必須去store中找。
加上namespace之後,每個mutaion屬於一個namespace,每個namespace代表一個module,在元件裡就可以輕鬆的根據namespace區分出哪個module來。
...mapGetters('aaaaa',[
'mutation111111',
'mutation22222',
'mutation33333'
]);
...mapMutations('aaaaa',[
'mutation111111',
'mutation22222',
'mutation33333'
]);
...mapMutations('bbbbb',[
'mutation4444444',
'mutation555555',
'mutation666666',
]);
複製程式碼
這樣重構之後,元件用到再多module的action、getter、mutation也不會混亂了。
3.mutation type和action type使用列舉常量約束
mutation type和action type的名字可能會寫錯,因為沒有使用typescript,沒有型別約束,如果寫錯了,編譯時無法檢查出來,只能在執行時檢查。解決這個問題或者使用ts,或者全部的mutation type和action type從列舉常量中取。
store中的資料是模組化的,mutation type 和action type的列舉常量自然也是,但是vuex的module並不會處理這兩者,想把這些模組化的motation type和action type掛到store例項上,可以通過vuex外掛來解決。
我發現社群並沒有我需要的vuex外掛,於是我自己封裝了一個
/**
* 生成檔案對應的模組
*
* @param {*} dirPath 資料夾路徑
*/
const generateModules = (files) => {
const modules = {}
files.keys().forEach(key => {
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
return modules;
}
/**
* 所有file
*
*/
const allFiles = {
page: require.context('../modules/page', false, /\.js$/),
components: require.context('../modules/components', false, /\.js$/),
domain: require.context('../modules/domain', false, /\.js$/),
other: require.context('../modules/other', false, /\.js$/)
}
/**
* 所有module
*
*/
const allModules = {
page: generateModules(allFiles.page),
components: generateModules(allFiles.components),
domain: generateModules(allFiles.domain),
other: generateModules(allFiles.other)
}
/**
* 根據types獲取modules下的多個模組的結構化資料
* @param {*} types module type
* @param {*} fieldName 欄位名
*/
const getStructuredData = (types, fieldNames) => {
const structuredData = {};
types.forEach(type => {
const modules = allModules[type];
const structuredModuleData = Object.keys(modules).map(moduleName => {
const fields = fieldNames.map(fieldName => modules[moduleName][fieldName])
return {
[moduleName]: Object.assign(...fields)
}
});
structuredData[type]= structuredModuleData && structuredModuleData.length ? Object.assign(...structuredModuleData): {};
})
return structuredData
}
const enumTypePlugin = store => {
const mutationTypeEnum = getStructuredData(['page','components','domain','other'], ['mutationTypes']);
const actionTypeEnum = getStructuredData(['page','components','domain','other'], ['actionTypes']);
store.mutationTypes = mutationTypeEnum;
store.actionTypes = actionTypeEnum;
}
module.exports = enumTypePlugin;
複製程式碼
新增到vuex的plugins中
import typeEnumPlugin from './type-enum-plugin';
new Vuex.Store(
modules,
plugins: [typeEnumPlugin]
)
複製程式碼
module定義時匯出mutation types和action types
module.exports = {
state,
getters,
mutations,
actions,
mutationTypes,
actionTypes
}
複製程式碼
在元件裡面就可以使用action type和mutation type來mapAction,mapMutation
...mapActions({
mutation1: this.$store.mutationTypes.page.aaa.mutation1,
mutation2: this.$store.mutationTypes.page.aaa.mutation2,
mutation3: this.$store.mutationTypes.page.aaa.mutation3
})
...mapActions({
action1: this.$store.actionTypes.page.aaa.action1,
action2: this.$store.actionTypes.page.aaa.action2,
action3: this.$store.actionTypes.page.aaa.action3
})
複製程式碼
或者像下面這樣全部匯入
...mapMutations(this.$store.mutationTypes.page.aaa)
...mapActions(this.$store.actionTypes.page.aaa)
複製程式碼
這樣就避免了手寫字串可能出錯的問題。
總結
針對vuex store的module過多,元件裡無法區分出getter、action、mutation屬於哪一個module,mutation type和action type無約束這3個問題,針對性的提出了3條解決方案:
module聚類分層,分成page、components、domain、other四個資料夾存放module;
新增namespace,元件中使用mapGetters、mapActions、mapMuatations時加上namespace區分;
module定義時匯出mutation types和action types,並通過vuex的外掛掛到store上,元件中使用mapMutations和mapActions不再通過字串取對應值。