Vuex中模块(Module)在复杂业务中的应用

模块(Module)是什么

Vuex在简单应用中的使用相信大家都会,但是当应用变得复杂时,store对象就可能变得臃肿,这时可以使用module解决这个问题。

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块(Module)动态注册

在前面的官方示例代码中,moduleA和moduleB都是预先知道的,那如果module无法预知呢?

需求:提供一个具有基础功能的版本,其他团队在此基础版上添加团队的定制功能

在这种需求下,Vuex的module设计采用动态注册(store.registerModule)是比较合适的。

// store.js
export default new Vuex.Store({

})

// components/base.vue 基础功能组件
<script>
import '../modules/base'
export default {
  ...
};
</script>

// modules/base.js 基础功能module
import baseStore from '../store'
let store = {
    state: {},
    mutations: {},
    actions: {
        init() {
            console.log('baseModule:init')
        }
    },
    getters: {}
}
baseStore.registerModule('base', store)


// components/team1.vue 团队1定制功能组件
<script>
import '../modules/team1'
export default {
  ...
};
</script>

// modules/team1.js 团队1定制功能module
import baseStore from '../store'
let store = {
    state: {},
    mutations: {},
    actions: {
        init() {
            console.log('team1Module:init')
        }
    },
    getters: {}
}
baseStore.registerModule('team1', store)

// 类似还有 team2和team3...

补充知识点一:卸载模块

你也可以使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。

补充知识点二:保留state

在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过 preserveState 选项将其归档:store.registerModule(‘a’, module, { preserveState: true })。

当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。

模块(Module)命名空间

问题:当基础功能和团队定制功能包含相同名称的action、mutation 和 getter时,会同时被触发

// components/base.vue
    this.$store.dispatch('init')

//输出
baseModule:init
team1Module:init
team2Module:init
team3Module:init

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

把store打印出来,对上面这句话会有更直观的感受

Vuex中模块(Module)在复杂业务中的应用_第1张图片

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

// components/base.vue
    // !!!注意:有命名空间时action名称需要加上registerModule时注册的模块名
    this.$store.dispatch('base/init')

// modules/base.js 基础功能module
import baseStore from '../store'
let store = {
    namespaced: true,
    ...
}
// 有命名空间时dispatch时需要带上这里传入的'base'前缀
baseStore.registerModule('base', store)


// modules/team1.js 团队1定制功能module
import baseStore from '../store'
let store = {
    namespaced: true,
    ...
}
// 同样,有命名空间时,dispatch时需要带上这里传入的'team1'前缀
baseStore.registerModule('team1', store)
// components/base.vue
    this.$store.dispatch('base/init')

//输出
baseModule:init

再来看下store的打印

Vuex中模块(Module)在复杂业务中的应用_第2张图片

模块(module)访问全局命名空间

需求:基础功能和定制功能需要获取权限信息进行管理

获取权限是个通用的功能,放在全局命名空间是比较合适的

//store.js
export default new Vuex.Store({
    actions:{
        getPermission() {
            console.log('request permission')
        }
    }
})

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。

// modules/base.js 基础功能module
import baseStore from '../store'
let store = {
    ...
    actions: {
        init({dispatch}) {
            console.log('baseModule:init')
            dispatch('getPermission',null,{root:true})
        }
    }
}

// modules/team1.js 团队1定制功能module
import baseStore from '../store'
let store = {
    ...
    actions: {
        init({dispatch}) {
            console.log('team1Module:init')
            dispatch('getPermission',null,{root:true})
        }
    }
}

store的插件机制

需求:对所有模块的dispatch接口调用进行数据统计

可以通过store的插件机制暴露出每个action的钩子。

//store.js
const myPlugin = store => {
    store.subscribeAction((action, state) => {
        console.log( 'report:', action.type)
    })
}
export default new Vuex.Store({
    plugins: [myPlugin],
    ...
})

补充知识点:subscribeAction 是在2.5.0新增的。mutation也是可以通过 subscribe 订阅的。

参考资料

  • Vuex Module

  • Vuex API参考

关于本文
作者:@红烧牛肉面
原文:https://github.com/masterkong/blog/issues/11

 热 文 推 荐 

☞ 7个有用的Vue开发技巧

☞ Serverless掀起新的前端技术变革

☞ GitHub上的项目受美国出口管制吗?

☞ Webpack 是怎样运行的?

☞ 面试加分项之 Nginx 反向代理与负载均衡

640?wx_fmt=png

640?wx_fmt=png

你也“在看”吗?

你可能感兴趣的:(Vuex中模块(Module)在复杂业务中的应用)