Skip to content
On this page

Vuex

Modules

1. Modules 使用时机

当项目足够庞大,值得分离模块时,就需要使用 Modules。

2. 不用与启用命名空间的使用方式对比

假设一个 module 叫mod,它的namespaced可以为true,也可以为false

js
export default {
  namespaced: true,
  state: {
    sta: 1,
  },
  mutations: {
    mut(state) {
      state.sta += 1;
    },
  },
  actions: {
    act({ commit }) {
      commit('mut');
    },
  },
  getters: {
    getsta(state) {
      return state.sta;
    },
  },
};

不用与启用命名空间的使用方式对比如下:

TIP

下文中的“不用”即不启用命名空间,“启用”即启用命名空间。

  • 读取 state

    • 不用:this.$store.state.mod.sta
    • 启用:同上
  • 读取 getter

    • 不用:this.$store.getters.getsta
    • 启用:this.$store.getters['mod/getsta']
  • 调用 commit

    • 不用:this.$store.commit('mut');
    • 启用:this.$store.commit('mod/mut')
  • 调用 dispatch

    • 不用:this.$store.dispatch('act');
    • 启用:this.$store.dispatch('mod/act')
  • mapState

    • 不用:...mapState({ sta: state => state.mod.sta })
    • 启用:...mapState('mod', ['sta'])...mapState('mod', { sta: 'sta' })...mapState('mod', { sta: state => state.sta })
  • mapGetters

    • 不用:...mapGetters(['getsta'])...mapGetters({ getsta: 'getsta' })
    • 启用:...mapGetters('mod', ['getsta'])...mapGetters('mod', { getsta: 'getsta' })
  • mapMutations

    • 不用:...mapMutations(['mut'])...mapMutations({ mut: 'mut' })
    • 启用:...mapMutations('mod', ['mut'])...mapMutations('mod', { mut: 'mut' })
  • mapActions

    • 不用:...mapActions(['act'])...mapActions({ act: 'act' })
    • 启用:...mapActions('mod', ['act'])...mapActions('mod', { act: 'act' })

对比结果:

  • 不用命名空间:

    • 优点:代码简单
    • 缺点:必须注意避免重名
  • 启用命名空间:

    • 优点:模块清晰
    • 缺点:代码量多,要记忆和书写模块名

结论:

  1. 全局的 State 、Getters、Mutations、Actions 写在 store.js 里。

  2. 非全局的 State 、Getters、Mutations、Actions 要写在模块里,且一定要启用命名空间,此种模块叫局部模块,这样优势是完全不必担心重名问题,也不用思考某个模块是否启用了命名空间,因为一定是启用了命名空间。

State

必须被 3 个以上组件使用,而且用常见的数据共享方式不够优雅时,才应考虑写入 Vuex。

Getters

❌ 错误实践:

一些集成框架在全局模块使用 Getters 来引用局部模块的 State,它的想法是可以将this.$store.state.mod.sta缩短成this.$store.getters.sta,可以少写几个字符。但是,这是错误的实践。

❓ 错误分析:

因为该框架从根本上没有正确利用全局模块和局部模块的各自优势,this.$store.state.mod.sta明显更清晰,既然故意使用了命名空间,为什么又要抹掉命名空间?而且有框架最神奇的操作是使用name: (state) => state.mod.name,这种做法有“三蠢”,第一蠢是使用name这个简单的通用词,第二蠢是不顾name重名风险,第三蠢是明明启用命名空间还尚且能区分,结果它偏要反其道而行之,故意抹掉命名空间,最后的希望也破灭了。

✔️ 正确实践:

  1. 在需要充当计算属性时,真正有计算过程代码时,才应使用 Getters。

  2. 虽然没有计算属性的意义,但是为了从一个对象中提取一个著名的属性,比如userName存在于state.userInfo.userName,又是常用属性,就值得添加一个 getters。

  3. 不要在 store.js 的 getters 中引用 Modules 里的 state,这会让特意构成的命名空间消失掉。

Mutations

  1. 必须只写与 State 相关的同步方法,且必须是同步方法。

  2. 遵守 Vuex 官方要求,方法名使用全大写蛇形结构。

  3. 修改 State:

  • 修改本模块的 State:直接操作,比如state.sta += 1
  • 修改其他模块的 State:这需要在 mutation 里调用其他模块的 Mutations,具体见下文。
  • 修改根的(或不用命名空间的模块的)State:这需要在 mutation 里调用根的 Mutations,具体见下文。
  1. 调用其他 Mutations(代码里的this指向 Vuex 实例):
  • 调用本模块的 Mutations:在自身 mutation 里写入this.commit('<本模块名>/<其他 mutation>');
  • 调用其他模块的 Mutations:在自身 mutation 里写入this.commit('<其他模块名>/<其他 mutation>');
  • 调用根的(或不用命名空间的模块的)Mutations:在自身 mutation 里写入this.commit('<根的 mutation>');

Actions

  1. 必须只写与 State 相关的异步方法,且必须是异步方法。

  2. 遵守 Vuex 官方要求,方法名使用全大写蛇形结构。

  3. 调用 Mutations(代码里的this指向 Vuex 实例):

  • 调用本模块的 Mutations:属于最常用写法,如commit('<其他 mutation>');
  • 调用其他模块的 Mutations:在自身 action 里写入 👍this.commit('<其他模块名>/<其他 mutation>');或 👎commit('<其他模块名>/<其他 mutation>', null, { root: true })
  • 调用根的(或不用命名空间的模块的)Mutations:在自身 action 里写入 👍this.commit('<根的 mutation>');或 👎commit('<根的 mutation>', null, { root: true })
  1. 调用其他 Actions(代码里的this指向 Vuex 实例):
  • 调用本模块的 Actions:常用写法,如dispatch('<其他 action>');
  • 调用其他模块的 Actions:在自身 action 里写入 👍this.dispatch('<其他模块名>/<其他 action>');或 👎dispatch('<其他模块名>/<其他 action>', null, { root: true })
  • 调用根的(或不用命名空间的模块的)Actions:在自身 action 里写入 👍this.dispatch('<根的 action>');或 👎dispatch('<根的 action>', null, { root: true })

跨模块调用

  1. 尽量避免跨模块调用。

  2. 使用this比使用{ root: true }更加简洁,同时不产生歧义,应推荐使用this

是否应使用 Computed

  1. 如果某个 State 、Getters、Mutations、Actions 在组件中没有复用 3 次以上,最好不要使用计算属性,因为:
  • 写计算属性需要代码横跳,从使用的位置横跳到computed,再回跳回来;

  • 变量名的语义不清晰,往往第一时间看不出来它来自于 Vuex 还是来自于data,比如this.sta可能来自于 Vuex 也可能来自于data,但是this.$store.state.mod.sta毫无疑问来自于 Vuex。

  1. 反之,如果某个 State 、Getters、Mutations、Actions 在组件中复用 3 次以上,可以考虑使用计算属性,但也不是绝对,毕竟$store.state.xxx全是优点,没有缺点。

杨亮的前端解决方案