Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
Action 函数接受一个 context 对象【与 store 实例具有相同方法和属性】context 对象不是 store 实例本身
你可以调用 context.commit
提交一个 mutation,
或者通过 context.state
和 context.getters
来获取 state 和 getters。
index.js中添加:
actions: { incrementAsync(context) { context.commit('increment'); //context.state.count //context.getters.countCache },
//实践中,我们会经常用到 ES2015 的 参数解构来简化代码(特别是我们需要调用 commit
很多次的时候):
decrementAsync({commit}) {
commit('decrement');
},
//实践中,我们会经常用到 ES2015 的 参数解构来简化代码(特别是我们需要调用 commit
很多次的时候):
incrementAsyncDelay({commit}) {
setTimeout(() => {
commit('increment');
}, 1000);//可以在 action 内部执行异步操作
},
// 用到载荷
decrementAsyncDelay(context, payload) { setTimeout(() => { console.log(payload); context.commit('decrement'); }, 1000);//可以在 action 内部执行异步操作 }, },
分发 Action
Action 通过 store.dispatch
方法触发:
store.dispatch('increment')
通过 载荷方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
通过 对象方式进行分发:
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
StoreComponent.vue:
<template> <div> <button @click="increment"> {{count}}</button> <button @click="decrement"> {{count}}</button> <span> Plus one: {{ countPlus }}</span> <span> Length: {{ countLength}}</span> <span> Plus one Length: {{ countPlusLength}}</span> <span> countAndCountPlusCache: {{ countPCountPlusCache}}</span> <span> countAndCountPlusFunc: {{ countPCountPlusFunc}}</span> <div> <span> countCache: {{ countCa}}</span> <span> countFunc: {{ countFu(12)}}</span> </div> <div> <table> <tr> <th>Product Name</th> <th>Product Count</th> <th>Product Price</th> <th>Product Total</th> </tr> <tr> <th>{{pName}}</th> <th>{{count}}</th> <th>{{pPrice}}</th> <th>{{pTotal}}</th> </tr> </table> </div> <div> <input type="text" v-model="productName"> <input type="text" v-model="productPrice"> <button @click="calculate({ type: 'calculate', price: productPrice, name: productName,})">Calculate </button> <button @click="incrementAsync">Increment Async</button> <button @click="decrementAsync">Decrement Async</button> <button @click="incrementAsyncDelay">Increment Async Delay</button> <button @click="decrementAsyncDelay('decrement test')">Decrement Async Delay</button> </div> </div> </template> <script> import {mapState} from 'vuex'; import {mapGetters} from 'vuex'; import {mapMutations} from 'vuex'; import {mapActions} from 'vuex'; export default { name: "StoreComponent", data: function () { return { productName: '', productPrice: 0, } }, // computed: { // count() { // return this.$store.state.count; // }, // // }, computed: { countPlusLength() { return this.countPlus.toString().length; }, countPCountPlusCache() { return this.$store.getters.countCache; }, countPCountPlusFunc() { return this.$store.getters.countFunc(2); }, ...mapState({ count: state => state.count, countPlus: 'countPlus', countLength(state) { return state.countLength; }, pName: 'productName', pPrice: 'productPrice', pTotal: 'productTotal', }), // 使用对象展开运算符将 getter 混入 computed 对象中 // ...mapGetters([ // 'countCache', // 'countFunc', // ]), ...mapGetters({ // 把 `this.countCa` 映射为 `this.$store.getters.countCache` countCa: 'countCache', // 把 `this.countFu` 映射为 `this.$store.getters.countFunc` countFu: 'countFunc', }), }, // computed: mapState([ // 'count', // 'countPlus', // 'countLength', // ]), methods: { increment() { this.$store.commit('increment'); }, decrement() { this.$store.commit('decrement'); }, // calculate() { // let payload = { // price: this.productPrice, // name: this.productName, // }; // // this.$store.commit('calculate', payload); // // // this.$store.commit({ // // type: 'calculate', // // price: this.productPrice, // // name: this.productName, // // }); // }, ...mapMutations([ 'calculate', ]), incrementAsync() {
//Action 通过 store.dispatch
方法触发
this.$store.dispatch('incrementAsync');
},
//mapActions
辅助函数
...mapActions([
// 'incrementAsync',
'decrementAsync',
'incrementAsyncDelay',
'decrementAsyncDelay',
]),
}
}
</script>
<style scoped>
</style>
效果:
组合 Action
Action 通常是异步的,那么如何知道 action 什么时候结束呢?
更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?
首先, store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch
仍旧返回 Promise:
类似于C#中返回的Task;
index.js:
import Vue from 'vue'; import 'es6-promise/auto' import Vuex from 'vuex'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 0, countPlus: 1, countLength: 1, productName: '', productPrice: 0, productTotal: 0, }, getters: { countCache: function (state) { return state.count + state.countPlus; }, countFunc: function (state) { return function (num) { return state.countPlus > num; } } }, mutations: { increment(state) { state.count++; state.countPlus = state.count + 1; state.countLength = state.count.toString().length; }, decrement(state) { state.count--; state.countPlus = state.count + 1; state.countLength = state.count.toString().length; }, calculate(state, payload) { state.productPrice = payload.price; state.productName = payload.name; state.productTotal = state.productPrice * state.count; }, }, actions: { incrementAsync(context) { context.commit('increment'); //context.state.count //context.getters.countCache }, decrementAsync({commit}) { commit('decrement'); }, async incrementAsyncDelay({commit}) { setTimeout(() => { commit('increment'); }, 1000); }, async decrementAsyncDelay(context, payload) { setTimeout(() => { console.log(payload); context.commit('decrement'); }, 1000); }, async comboAsyncDelay({dispatch, commit}) { await dispatch('incrementAsyncDelay'); commit('decrement', await setTimeout(() => { console.log('another task'); }, 1000)); }, }, }); export default store;
主要是:
添加了async标识,await异步操作。
StoreComponent.vue:
<template> <div> <button @click="increment"> {{count}}</button> <button @click="decrement"> {{count}}</button> <span> Plus one: {{ countPlus }}</span> <span> Length: {{ countLength}}</span> <span> Plus one Length: {{ countPlusLength}}</span> <span> countAndCountPlusCache: {{ countPCountPlusCache}}</span> <span> countAndCountPlusFunc: {{ countPCountPlusFunc}}</span> <div> <span> countCache: {{ countCa}}</span> <span> countFunc: {{ countFu(12)}}</span> </div> <div> <table> <tr> <th>Product Name</th> <th>Product Count</th> <th>Product Price</th> <th>Product Total</th> </tr> <tr> <th>{{pName}}</th> <th>{{count}}</th> <th>{{pPrice}}</th> <th>{{pTotal}}</th> </tr> </table> </div> <div> <input type="text" v-model="productName"> <input type="text" v-model="productPrice"> <button @click="calculate({ type: 'calculate', price: productPrice, name: productName,})">Calculate </button> <button @click="incrementAsync">Increment Async</button> <button @click="decrementAsync">Decrement Async</button> <button @click="incrementAsyncDelay">Increment Async Delay</button> <button @click="decrementAsyncDelay('decrement test')">Decrement Async Delay</button> <button @click="comboAsyncDelay">Combo Async Delay</button> </div> </div> </template> <script> import {mapState} from 'vuex'; import {mapGetters} from 'vuex'; import {mapMutations} from 'vuex'; import {mapActions} from 'vuex'; export default { name: "StoreComponent", data: function () { return { productName: '', productPrice: 0, } }, // computed: { // count() { // return this.$store.state.count; // }, // // }, computed: { countPlusLength() { return this.countPlus.toString().length; }, countPCountPlusCache() { return this.$store.getters.countCache; }, countPCountPlusFunc() { return this.$store.getters.countFunc(2); }, ...mapState({ count: state => state.count, countPlus: 'countPlus', countLength(state) { return state.countLength; }, pName: 'productName', pPrice: 'productPrice', pTotal: 'productTotal', }), // 使用对象展开运算符将 getter 混入 computed 对象中 // ...mapGetters([ // 'countCache', // 'countFunc', // ]), ...mapGetters({ // 把 `this.countCa` 映射为 `this.$store.getters.countCache` countCa: 'countCache', // 把 `this.countFu` 映射为 `this.$store.getters.countFunc` countFu: 'countFunc', }), }, // computed: mapState([ // 'count', // 'countPlus', // 'countLength', // ]), methods: { increment() { this.$store.commit('increment'); }, decrement() { this.$store.commit('decrement'); }, // calculate() { // let payload = { // price: this.productPrice, // name: this.productName, // }; // // this.$store.commit('calculate', payload); // // // this.$store.commit({ // // type: 'calculate', // // price: this.productPrice, // // name: this.productName, // // }); // }, ...mapMutations([ 'calculate', ]), incrementAsync() { this.$store.dispatch('incrementAsync').then(() => { console.log('Increment Success!'); }); }, ...mapActions([ // 'incrementAsync', 'decrementAsync', 'incrementAsyncDelay', 'decrementAsyncDelay', 'comboAsyncDelay', ]), } } </script> <style scoped> </style>
以及:
还有一个incrementAsync测试