Actions
Action 跟 mutation 类似,两者的区别是:
- Mutation 修改状态,action 则提交(一个或多个) mutation。
- Action 可以随意包含异步操作。
我们来注册一个简单的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 处理器接收一个上下文对象(作为参数),该对象提供了跟 store 实例一样的 方法/属性,所以你可以调用 context.commit
来提交一个 mutation,或者访问 context.state
来访问状态和 getter。我们在稍后介绍的 modules 章节会看到为什么这个上下文对象不是 store 实例本身。
In practice, we often use ES2015 argument destructuring to simplify the code a bit (especially when we need to call commit
multiple times):
事实上,我们经常使用 ES2015 的 参数解构 来简化一点点代码 (特别是多次调用 commit
的时候):
actions: {
increment ({ commit }) {
commit('increment')
}
}
分发 Actions
Action 通过 store.dispatch
方法来触发:
store.dispatch('increment')
咋看起来有点无语:如果我们要增加计数,为什么不直接简单地调用 store.commit('increment')
呢?mutation 必须是同步的,action 则不须要,记起来了吗?我们可以在 action 内执行 异步 操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions support the same payload format and object-style dispatch: Action 分发同样支持参数对象格式和对象风格:
// 带有参数对象的分发
store.dispatch('incrementAsync', {
amount: 10
})
// 通过一个对象来分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
一个更实际的 action 例子:action 用来结算购物车,它包含了 调用异步 API 和 提交多个 mutation:
actions: {
checkout ({ commit, state }, payload) {
// 把当前商品保存到商品列表
const savedCartItems = [...state.cart.added]
// 发出结算请求,然后乐观地处理
// 清空购物车
commit(types.CHECKOUT_REQUEST)
// shop API 接收一个成功和一个失败的回调
shop.buyProducts(
products,
// 处理成功 success
() => commit(types.CHECKOUT_SUCCESS),
// 处理失败
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
要注意的是,我们正在执行一些异步操作的工作流,然后通过提交 mutation 来记录 action 引起的副作用(状态变更)。
组件内分发 Action
在组件中,可以通过 this.$store.dispatch('xxx')
来分发 action,或者是用 mapActions
工具来映射组件方法到 store.dispatch
的调用(需要根实例配置 store
):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment' // 映射 this.increment() 为 this.$store.dispatch('increment')
]),
...mapActions({
add: 'increment' // 映射 this.add() 为 this.$store.dispatch('increment')
})
}
}
组合多个 Action
Action 常常是异步的,所以我们如何知道一个 action 已经完成?还有更重要的,如何组合多个 action 来处理更复杂的异步工作流?
首先要知道的是,store.dispatch
会将 action 处理器返回的值作为返回值,所以你可以在 action 处理器返回一个 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
于是你可以:
store.dispatch('actionA').then(() => {
// ...
})
在另一个 action 也一样:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,如果我们使用 async / await,一个很快会成标准的 JavaScript 特性,我们就能像下面这样组合我们的 action:
// 结社 getData() 和 getOtherData() 返回 Promises
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
对于
store.dispatch
来说,有可能在不同模块间触发多个 action。这种情况下,返回的值得是一个 Promise,它会在所有触发的 action 处理器都被 resolve 完之后再 resolve。