本篇文章仅适合基础回顾,不具备深度

之前已经写了 vuex 的基本定义和使用,以及 Store、State、Getter 的相关内容,文章地址:

一、Mutation

之前文章已经说明了获取 state 的方式有两种,分别是 this.$store.state.num 这种直接获取的方式,以及通过 getter 定义的方式获取 this.$store.getter.num

而修改 state 不能直接修改对象或者覆盖对象的属性,因为我们遵循的是单一状态树的管理原则,不允许通过 this.$store.state.num = 3 修改 state。

定义一个 mutation:(已经存在 state.count

const mutations = {
  increment(state){
    state.count ++ ;
  },
}

定义的 mutations 对象将挂载到 Store 的 mutations 属性上。

mutations 的每个属性都是以方法的形式定义,默认接收一个参数,而这个参数实际上就是 Store 的 state 对象,只有在 mutations 的属性中直接通过 state.xxx = xxx 修改 state。

而 mutations 的方法也不是直接通过 this.$store.mutations.xxx(xx) 去调用的,而是通过主动触发的。

可以打印 this.$store 查看 Store 的属性,可以发现, mutations 是以 _mutations 的私有属性形式存在的,因此并不能直接调用(不能是指不允许)。

1.jpg

从上面的属性列表中可以发现 commit 属性,而这个属性是一个 function,用来触发 mutations 中定义的 mutation,比如上面定义了 increment,可以通过以下方式触发 mutation:

this.$store.commit('increment');

效果:

1.gif

1、playload 传参

上面定义的 increment mutation 实际上是没有传递参数的, state 是默认的参数,mutation 传参在 vuex 中称为 payload。

下面的 incrementPlayload mutation 传递了额外的参数 num2,作用是每次触发 incrementPlayload 的时候,state.count 不是自增 1 而是加上 num2 :

  incrementPlayload(state, num2) {
    return state.count = state.count + num2;
  }

playload 的触发形式与之前的不同之处在于多传递一个参数而已:

this.$store.commit('incrementPlayload', 2);

上面触发 incrementPlayload 之后效果如下:

2.gif

需要注意的是,playload 只支持一个参数,如果写了多个参数 (state, num1, num2) 是无效的

因此一般建议是通过对象的方式定义 playload,然后通过对象的方式传递 playload,这样子既能够多传递一些数据,也便于组件开发者的理解,比如

incrementPlayloadObj(state, playload) {
  return state.count = state.count + playload.num;
}

而在触发的时候,如下传递参数即可:

this.$store.commit('incrementPlayloadObj', {num: 2});

而针对上面这种情况,也可以给 commit 直接传递一个对象,只需要一个对象参数即可:

this.$store.commit({ type: 'incrementPlayloadObj', num: 2});

type 是固定参数,用来指定触发哪个 mutation,这种形式比较尴尬的就是 type 属性已经是预置被用掉了。

2、一些约定或者是规范

  • mutation 中如果是修改对象则必须遵循 vue 的响应原则, 也就是全覆盖。

这里的对象包括了 array,如果修改 object array 一般都是通过 Object.assign({},obj1,obj2) 的形式或者 array 的重新赋值整个新的 array,不能直接修改属性。

  • mutation 中必须是同步操作

mutation 中如果是异步方法,会导致在触发 mutation 的时候回调还没有触发,因此不知道什么时候去改变状态,对于状态的改变是无法追踪的。

  • mutation 的名称一般建议使用常量代替字符串

这也是 FLUX 架构推荐的方式,不过多阐述其好处,坏处就是各种引入引出。

3、mapMutation

mapMutations 类似于 mapState mapGetters 也是一种辅助方法,能够在某些程度上简化代码。

以下代码来文档,在 组件 中直接映射到 methods 中,可以重命名等。

import { mapMutations } from 'vuex'

export default {
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ])
  }
}

二、Action

上面提到 mutations 中不能存在 异步操作,因为无法准备的进行状态变更的追踪,会造成意想不到的效果。

而 Action 则是在 mutation 之前(可以这么理解)进行的操作,Action 最终是对 mutation 进行 commit,因此 Action 并不直接修改 state,因此 Action 中可以存在任何的异步操作。

actions 的定义类似于 其他几个属性,只不过默认的参数不一样,默认带过来的是与 Store 有相同属性和方法的对象,并不直接就是 Store(这是因为如果通过 module 分割 store 的话,每个 module 的 action 的 context 中并不是 store 对象):

打印 context 对象可以发现:

2.jpg

const actions = {
  incrementAction(context) {
    console.log(context);
    context.commit('increment');
  }
}

上面的 action 中实际上是最终触发了 increment 这个 mutation。

触发 Action,Store 的属性和方法中, commit() 是用来触发 mutation 的,而 dispatch() 则是用来触发 action 的:

this.$store.dispatch('incrementAction');

1、Action playload

action 的 playload 与 mutation 一模一样,比如一个参数从 action 再传递到 mutation (因为 mutation 是最终修改 state 的地方):

  incrementActionPlayload(context, playload) {
    console.log(context);
    store.commit('incrementPlayloadObj', {
      num: playload.num2
    });
  }

而在触发的时候,传递参数的方式也是一样:

this.$store.dispatch('incrementActionPlayload', {num : 2});

效果:

3.gif

2、异步 action

如果存在异步操作比如数据请求,则需要放在 action 中:

  incrementAction(context) {
    setTimeout(() => {
      context.commit('increment');
    }, 500);
  },

触发的方式不发生任何变化,效果如下:

44.gif

3、mapActions

mapActions 也是为了方便在组建中去重新定义和使用 Actions,与 mapState mapGetters mapMutations 一样的用法就不重复了。

4、Actions 的组合

作为的组合就是两个 actions 可能相互依赖,比如 actionB 依赖于 actionA 之后,则可以通过 Promise 的方式去实现:

  • actionA commit 之后返回一个 Promise.resolve
  • actionB 中,先 dispatch('actionA') 然后在 dispatch('actionA').then() 中去 commit mutation。

这样的好处是,如果要 dispatch(actionB),则会首先 dispatch(actionA),而单独 dispatch(actionA) 又不会对 actionA 造成什么影响。