層次和約束:專案中使用vuex的3條優化方案
使用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聚類分層,新增namespace,module定義時匯出mutation type和action type,並通過vuex的外掛掛到store上。