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 纔會執行。
參考: