# Actions

Actions 是用于 dispatch mutations 的函数。Actions 可以是异步的，一个 action 可以 dispatch 多个 mutations.

一个 action 描述了有什么事情应该发生，把本应该在组件中调用的逻辑细节抽象出来。当一个组件需要做某件事时，只需要调用一个 action —— 组件本身并不需要关心具体的后果：不需要提供回调函数也不需要期待返回值，因为 actions 的结果一定是 state 产生了变化，而 state 一旦变化，便会触发组件的 DOM 更新。 这样，组件便完全和 action 的具体逻辑解耦了。

因此，我们通常在 actions 中做 API 相关的请求。通过 actions 的封装，我们使得组件和 mutations 都不需要关心这些异步逻辑。

> Vuex actions 和 Flux 中的 "action creators" 是等同的概念，但是我觉得这个定义常让人感到困惑（比如分不清 actions 和 action creators）。

### 简单的 Actions

最简单的情况下，一个 action 即触发一个 mutation。Vuex 提供一个快捷的方式去定义这样的 actions:

``` js
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    INCREMENT (state, x) {
      state += x
    }
  },
  actions: {
    // 快捷定义
    // 只要提供 mutation 名
    increment: 'INCREMENT'
  }
})
```

调用 action:

``` js
store.actions.increment(1)
```

这相当于调用：

``` js
store.dispatch('INCREMENT', 1)
```

注意所有传递给 action 的参数同样会传递给 mutation handler.

### 正常 Actions

对于包含逻辑或是异步操作的 actions，则用函数来定义。Actions 函数获得的第一个参数永远是其所属的 store 实例：

``` js
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    INCREMENT (state, x) {
      state += x
    }
  },
  actions: {
    incrementIfOdd: (store, x) => {
      if ((store.state.count + 1) % 2 === 0) {
        store.dispatch('INCREMENT', x)
      }
    }
  }
})
```

通常我们会用 ES6 的参数解构 (arguments destructuring) 语法来使得函数体更简洁：

``` js
// ...
actions: {
  incrementIfOdd: ({ dispatch, state }, x) => {
    if ((state.count + 1) % 2 === 0) {
      dispatch('INCREMENT', x)
    }
  }
}
```

同时，简单 actions 的快捷定义其实只是如下函数的语法糖：

``` js
actions: {
  increment: 'INCREMENT'
}
// ... 上面的定义等同于：
actions: {
  increment: ({ dispatch }, ...payload) => {
    dispatch('INCREMENT', ...payload)
  }
}
```

### 异步 Actions

异步 actions 同样使用函数定义：

``` js
// ...
actions: {
  incrementAsync: ({ dispatch }, x) => {
    setTimeout(() => {
      dispatch('INCREMENT', x)
    }, 1000)
  }
}
```

举个更实在的例子，比如一个购物车。当用户结账时，我们可能需要在 checkout 这一个 action 中触发多个不同的 mutations：一个在开始检查购物车时触发，一个在成功后触发，还有一个在失败时触发。

``` js
// ...
actions: {
  checkout: ({ dispatch, state }, products) => {
    // 保存结账前的购物车内容
    const savedCartItems = [...state.cart.added]
    // 发出结账的请求，并且清空购物车
    dispatch(types.CHECKOUT_REQUEST)
    // 假设我们的后台 API 接受一个成功回调和一个错误回调
    shop.buyProducts(
      products,
      // 结账成功
      () => dispatch(types.CHECKOUT_SUCCESS),
      // 结账失败，将购物车恢复到结账之前的状态
      () => dispatch(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}
```

这里有相对复杂的异步逻辑，但是购物车的组件依然只需要简单地调用 `store.actions.checkout(products)` 即可.
