actions.md 3.6 KB

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:

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

调用 action:

store.actions.increment(1)

这相当于调用:

store.dispatch('INCREMENT', 1)

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

正常 Actions

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

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) 语法来使得函数体更简洁:

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

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

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

异步 Actions

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

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

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

// ...
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) 即可.