vuex 版本为
^2.3.1
,按照我本身的理解来整理vuex。
Action 相似于 mutation,不一样在于:vue
个人理解就是,mutation是一把刀,action 是一我的,这我的能够同步耍刀,也能够异步耍刀,可是刀只能同步劈或者切或者砍。
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { // context 对象的使用跟 store 对象的使用相似 increment (context) { // 直接能够 commit(原来是this.$store.commit) context.commit('increment') } // 在 es2015下,可使用参数解构的写法 increment ({ commit }) { //直接解构出 commit 来进行 mutation的提交 commit('increment') } } })
Action 函数接受一个与 store 实例具备相同方法和属性的 context 对象,所以你能够调用 context.commit
提交一个 mutation,或者经过 context.state
和 context.getters
来获取 state 和 getters。git
这是 jsrun 的例子: https://jsrun.net/avqKp
备注:参数解构参考地址https://github.com/lukehoban/es6features#destructuring,参数解构能够将对象或者数组按照必定的规则解构出来直接使用。es6
以前说过,mutation 必须同步执行,但 action 不须要,因此二者结合使用,可以实现一个可以异步执行的 mutation。github
形象地来讲就是异步执行的 action 去操做同步执行的 mutation。
// 初始化 action actions: { // 异步操做 incrementAsync ({ commit }) { setTimeout(() => { // 异步 commit commit('increment') }, 1000) } }
// 通常形式分发 action store.dispatch('increment') // 以载荷形式分发 store.dispatch('incrementAsync', { amount: 10 }) // 以对象形式分发 store.dispatch({ // 传入包含 type 属性的对象(相似 mutation) type: 'incrementAsync', amount: 10 })
异步分发样例:vuex
actions: { // 解构 context 对象里面的 commit 和 state 来使用 checkout ({ commit, state }, products) { // 把当前购物车的物品备份起来 const savedCartItems = [...state.cart.added] // 清空购物车 commit(types.CHECKOUT_REQUEST) // 购物 API 接受一个成功回调和一个失败回调 shop.buyProducts( products, // 成功操做 () => commit(types.CHECKOUT_SUCCESS), // 失败操做 () => commit(types.CHECKOUT_FAILURE, savedCartItems) ) } }
这里整个流程是:segmentfault
这个例子里面其实主要是说明能够异步操做,其余的逻辑能够暂时不用理会。
你在组件中使用 this.$store.dispatch('xxx')
分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch
调用(须要先在根节点注入 store):后端
这个就很相似以前的mapMutations了
首先:normalizeMap会将actions格式化为一个数组:数组
function normalizeMap (map) { // 判断是否数组,而且最终返回也是一个数组 return Array.isArray(map) // 是数组就直接 map 循环 ? map.map(key => ({ key, val: key })) // 是对象就将 key拿出来,而后再进行 map 循环 : Object.keys(map).map(key => ({ key, val: map[key] })) }
例如传入的actions 是一个数组,以下:promise
// 转换前 [ // 这是没额外参数的(没载荷) 'increment', // 这是有额外参数的(有载荷) 'incrementBy' ] // 那么被normalizeMap转换后: // 即转换为{ key, val: key }) [ { key, // key 是increment val: key // val是increment }, // 这里虽说有额外参数传入,可是这个参数并无在转换中处理 { key, // key 是incrementBy val: key // val是incrementBy }, //..... ]
例如传入的actions 是一个对象,以下:服务器
// 转换前 { add: 'increment' } // 那么被normalizeMap转换后: // 即转换为{ key, val: key }) { key, // key 是addAlias val: map[key] // val 是对象的 key 属性的值,就是 'increment' }
而后看回去 vuex 的源代码关于mapActions的部分:
var mapActions = normalizeNamespace(function (namespace, actions) { var res = {}; normalizeMap(actions).forEach(function (ref) { var key = ref.key; var val = ref.val; res[key] = function mappedAction () { // 也是跟 mapmutation相似,获取载荷参数 var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; // 保存当前 vuex 的 dispatch 方便后面处理 var dispatch = this.$store.dispatch; // 省略命名空间部分 return typeof val === 'function' // 是函数就直接执行 ? val.apply(this, [dispatch].concat(args)) // 不是函数就用 dispatch 执行 : dispatch.apply(this.$store, [val].concat(args)) }; }); return res });
dispatch 是 action 执行的固定语法,跟 mutation 的 commit 相似
那么回归到实际转换效果,以下:
// 须要引入mapActions才可使用 import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` 'increment', // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` 'incrementBy' ]), // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` ...mapActions({ add: 'increment' }) } }
对比着 mutation 来看,就很是好理解了。
这是 jsrun 的例子:https://jsrun.net/jwqKp
Action 一般是异步的,那么如何知道 action 何时结束呢?更重要的是,咱们如何才能组合多个 action,以处理更加复杂的异步流程?
首先,你须要明白 store.dispatch
能够处理被触发的 action 的处理函数返回的 Promise,而且 store.dispatch
仍旧返回 Promise:
换言之,就是
store.dispatch
可以处理 promise,而且也会返回 promise,因此可以在异步中处理逻辑。
// 初始化 actions actions: { actionA ({ commit }) { // 返回一个 promise 对象 return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() // 跟通常 promise 的使用差异不大 }, 1000) }) } } // 使用 actions store.dispatch('actionA').then(() => { // 可使用 then 了 // ... }) // 在 actionB 里面分发 actionA actions: { // ... actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) } }
总的来讲就是 action 支持返回一个 promise 来作处理,这样就能够很好的使用 promise 来进行异步操做了。
若是咱们利用 async / await
,咱们能够以下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
ES2017 标准引入了 async 函数,async 函数会让异步代码更加直观,若是不用能够无论。
须要注意的是,一个
store.dispatch
在不一样模块中能够触发多个 action 函数。在这种状况下,只有当全部触发函数完成后,返回的 Promise 才会执行。
参考: