Mutations

在 Vuex 商店中实际改变状态的唯一方法是提交一个 mutation。Vuex mutations 非常类似于事件:每个 mutation 都有一个字符串 **type** 和一个 **handler**。handler 函数是我们在其中执行实际状态修改的地方,它将接收状态作为第一个参数

const store = createStore({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // mutate state
      state.count++
    }
  }
})

你不能直接调用 mutation handler。把它想象成更像事件注册:“当一个类型为 increment 的 mutation 被触发时,调用这个 handler。” 为了调用 mutation handler,你需要使用它的类型调用 store.commit

store.commit('increment')

带 Payload 的提交

你可以向 store.commit 传递一个额外的参数,它被称为 mutation 的 **payload**

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多数情况下,payload 应该是一个对象,以便它可以包含多个字段,并且记录的 mutation 也将更具描述性

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

对象风格提交

提交 mutation 的另一种方法是直接使用一个具有 type 属性的对象

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格提交时,整个对象将作为 payload 传递给 mutation handler,因此 handler 保持不变

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

使用常量作为 Mutation 类型

在各种 Flux 实现中使用常量作为 mutation 类型是一种常见的模式。这允许代码利用像 linter 这样的工具,并且将所有常量放在一个文件中可以让你的合作者一目了然地了解整个应用程序中可能出现的 mutation

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = createStore({
  state: { ... },
  mutations: {
    // we can use the ES2015 computed property name feature
    // to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

是否使用常量很大程度上取决于个人喜好 - 它在拥有许多开发人员的大型项目中可能会有所帮助,但如果你不喜欢它们,它完全是可选的。

Mutations 必须是同步的

要记住的一条重要规则是 **mutation handler 函数必须是同步的**。为什么?考虑以下示例

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

现在想象我们正在调试应用程序并查看 devtool 的 mutation 日志。对于记录的每个 mutation,devtool 将需要捕获状态的“之前”和“之后”快照。然而,上面示例 mutation 中的异步回调使得这变得不可能:当 mutation 被提交时,回调还没有被调用,devtool 无法知道回调何时会被实际调用 - 在回调中执行的任何状态 mutation 本质上是不可追踪的!

在组件中提交 Mutations

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数,它将组件方法映射到 store.commit 调用(需要根 store 注入)

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // map `this.increment()` to `this.$store.commit('increment')`

      // `mapMutations` also supports payloads:
      'incrementBy' // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // map `this.add()` to `this.$store.commit('increment')`
    })
  }
}

继续 Actions

异步操作与状态 mutation 相结合会使你的程序非常难以理解。例如,当你调用两个方法,它们都带有异步回调来修改状态时,你怎么知道它们何时被调用以及哪个回调先被调用?这正是我们想要将这两个概念分开的理由。在 Vuex 中,**mutation 是同步事务**

store.commit('increment')
// any state change that the "increment" mutation may cause
// should be done at this moment.

为了处理异步操作,让我们介绍一下 Actions.