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)。
最简单的情况下,一个 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 函数获得的第一个参数永远是其所属的 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: {
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)
即可.