背景
常规的vuex模块是放在 store/modules/ 下
- 与业务分隔严重,很难保证维护者不会引入其他业务需求代码;
- 并且长期以往代码量积累,后期难以维护;
vuex动态注册优势
- vuex模块存放地点 (就近管理,存在于业务模块根目录/__store) 更加贴近对应的需求代码;
- 可以在离开业务模块后注销相应的vuex模块,不会对其他业务模块造成影响;
基础使用
业务模块中
- 在created中注册模块;如果需要,在beforeDestroy中注销模块;
- 需要获得store主模块的引用
- 需要考虑子模块是否重复注册问题
import store from '@/store'
import customModule from './__sotre'
export default {
created() {
store.registerModule('customModule', customModule)
},
beforeDestroy() {
store.unregisterModule('customModule')
},
}
store子模块中
建议使用函数的形式初始化state,保证如果注销后再次注册数据是干净的
// 使用函数初始化state,保证每次注册都是干净的值
function initialState() {
return {
testData: [],
}
}
export default {
namespaced: true,
state: initialState,
}
优化思路
通过Vue.use插件形式进行封装,满足以下需求
- 模块名校验,统一以Module结尾,保证与主模块名不会冲突
- 检测子模块是否重复注册
- 让 注销模块功能 跟随组件销毁自动执行,并通过可选参数取消
期望使用方式
import customModule from './__sotre'
export default {
created() {
this.$registerModule('customModule', customModule)
},
}
注册实例属性
需要将store主模块的引用传入
export default {
install(Vue, options) {
// 注册store模块时,需要主模块的引用
const {store} = options
Vue.prototype.$registerModule = registerModule
}
}
模块名校验
需要通过组件实例调用 $registerModule ,因为在自动注销模块是需要使用组件实例
function registerModule(moduleName, moduleStore, options = {}) {
if (!(this instanceof Vue)) {
throw new Error('请在组件内使用this.$registerModule')
}
const moduleRegexp = new RegExp(/Module$/)
if (!moduleRegexp.test(moduleName)) {
throw new Error(`${moduleName} 模块命名请以Module结尾`)
}
...
}
模块重复注册
vuex本身带有检测模块是否注册接口hasModule,但却是从3.2.0版本开始支持,这里需要向下兼容
function registerModule(moduleName, moduleStore, options = {}) {
...
// vuex自带支持 hasModule 的API从3.2.0新增
// https://vuex.vuejs.org/zh/api/#hasmodule
const moduleNames = []
let hasModule = false
if (store.hasModule) {
hasModule = store.hasModule(moduleName)
} else {
hasModule = moduleNames.includes(moduleName)
}
if (hasModule) {
console.warn(`模块${moduleName}已经注册`)
} else {
// 注册模块
store.registerModule(moduleName, moduleStore)
}
...
}
自动注销模块
默认在业务场景离开后会自动注销store子模块,如果需要持久化的话,设置 autoUnregister
- 借助Vue中组件在执行生命周期钩子函数时,会调用相应的 hook: 事件,通过监听 hook:beforeDestroy 事件,实现自动注销子模块
- 在模块注销时,需要将依赖全部清除才可以注销,详情请查看
function registerModule(moduleName, moduleStore, options = {}) {
...
// 自动注销模块,如果需要store持久化,传入
const {autoUnregister = true} = options
if (autoUnregister) {
moduleNames.push(moduleName)
this.$on('hook:beforeDestroy', unregisterModule.bind(this, moduleName))
}
}
function unregisterModule(moduleName) {
// 清除moduleName
const idx = moduleNames.indexOf(moduleName)
moduleNames.splice(idx, 1)
// 需要完全清除依赖才注销
// https://github.com/XyyF/vuex-register-module-demo/issues/1
if (!moduleNames.includes(moduleName)) {
store.unregisterModule(moduleName)
}
}
完整代码
const moduleNames = []
export default {
install(Vue, options) {
const {store} = options
// 添加实例上的引用
Vue.prototype.$registerModule = registerModule
function registerModule(moduleName, moduleStore, options = {}) {
if (!(this instanceof Vue)) {
throw new Error('请在组件内使用this.$registerModule')
}
const moduleRegexp = new RegExp(/Module$/)
if (!moduleRegexp.test(moduleName)) {
throw new Error(`${moduleName} 模块命名请以Module结尾`)
}
// vuex自带支持 hasModule 的API从3.2.0新增
// https://vuex.vuejs.org/zh/api/#hasmodule
let hasModule = false
if (store.hasModule) {
hasModule = store.hasModule(moduleName)
} else {
hasModule = moduleNames.includes(moduleName)
}
if (hasModule) {
console.warn(`模块${moduleName}已经注册`)
} else {
// 注册模块
store.registerModule(moduleName, moduleStore)
}
// 自动注销模块,如果需要store持久化,传入
const {autoUnregister = true} = options
if (autoUnregister) {
moduleNames.push(moduleName)
this.$on('hook:beforeDestroy', unregisterModule.bind(this, moduleName))
}
}
function unregisterModule(moduleName) {
// 清除moduleName
const idx = moduleNames.indexOf(moduleName)
moduleNames.splice(idx, 1)
// 需要完全清除依赖才注销
// https://github.com/XyyF/vuex-register-module-demo/issues/1
if (!moduleNames.includes(moduleName)) {
store.unregisterModule(moduleName)
}
}
},
}