action類似於mutation,不同的是Action提交的是mutation,而不是直接變更狀態,而且action里可以包含任意異步操作,每個mutation的參數1是一個對象,可以包含如下六個屬性:
commit ;當前命名空間對應的commit
dispatch ;當前命名空間對應的dispatch
state ;當前命名空間對應的state
getters ;當前命名空間對應的getters
rootState ;根模塊的state
rootGetters ;根模塊的getters
類似於mutation,創建Vuex.Store()倉庫實例時可以通過actions創建每個action
我們也不能直接調用一個action,而是通過 store.dispatch來調用,dispatch可以帶兩個參數,如下:
type ;對應的action名
payload ;傳入的參數
dispatch還有一種寫法,就是傳入一個對象即可,該對象可以帶一個type參數,type指定為action的名稱,整個對象會作為參數傳遞給action。注意:action里可以包含異步操作
例如:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/vuex@3.1.0/dist/vuex.js"></script> </head> <body> <div id="app"> <p>{{no}}</p> <button @click="test">測試</button> </div> <script> const store = new Vuex.Store({ state:{no:100}, mutations:{ increment(state,payload){state.no+=payload.no;} }, actions:{ increment({commit},info){ return new Promise(function(resolve,reject){ //action里返回一個Promise對象 setTimeout(function(){ commit('increment',info) resolve('ok') },500) }) } } }) var app = new Vue({ el:"#app", store, computed:{ no(){return this.$store.state.no} }, methods:{ test(){ this.$store.dispatch('increment',{no:100}) } } }) </script> </body> </html>
我們在action里不返回一個promise對象也可以,vuex內部會調用Promise.resolve自動將返回值轉換為一個Promise對象
源碼分析
writer by:大沙漠 QQ:22969969
在創建Vuex.Store()初始化時會執行installModule()安裝根模塊,和mutation相關的如下:
function installModule (store, rootState, path, module, hot) { //安裝模塊 /*略*/ module.forEachAction(function (action, key) { //遍歷module模塊的action對象,如果找到了,則執行這個匿名函數 參數1:每個action值 key:對應的鍵名 var type = action.root ? key : namespace + key; //對應的命名空間+key var handler = action.handler || action; //獲取對應的函數 registerAction(store, type, handler, local); //調用registerAction注冊action }); /*略*/ }
registerAction是用於注冊action的,如下:
function registerAction (store, type, handler, local) { //注冊action函數 store:Store實例 type:包含命名空間的action名 handler:函數 local:上下文相關的對象 var entry = store._actions[type] || (store._actions[type] = []); //如果store對象的_actions對應的type為空,則初始化為空數組 entry.push(function wrappedActionHandler (payload, cb) { //給store._actions push 進去一個匿名函數 var res = handler.call(store, { //該函數 dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }, payload, cb); //執行handler函數,上下文為store,參數1是個對象,參數2是payload數據,將返回值保存到res中 if (!isPromise(res)) { //如果res不是一個Promise res = Promise.resolve(res); //則將它轉換為Promise對象 } if (store._devtoolHook) { return res.catch(function (err) { store._devtoolHook.emit('vuex:error', err); throw err }) } else { return res } }); }
從這里我們可以看到每個action對應的參數1,就是這里執行的handler函數,傳入的對象,返回值如果不是Promise對象,則調用Promise.resolve()將它轉換為Promise對象
等我們去調用this.$store.dispatch('increment',{no:100})觸發一個action時,首先會觸發Store函數內重定義的dispatch,它會以當前Store函數對象為上下文繼續執行Store原型上的dispatch函數,如下:
Store.prototype.dispatch = function dispatch (_type, _payload) { //派發一個action異步操作 var this$1 = this; // check object-style dispatch var ref = unifyObjectStyle(_type, _payload); //規范一下參數,返回一個對象,這里和commit調用的是一樣的 var type = ref.type; var payload = ref.payload; var action = { type: type, payload: payload }; var entry = this._actions[type]; //嘗試獲取type類型的action if (!entry) { //如果不存在則報錯並返回 { console.error(("[vuex] unknown action type: " + type)); } return } try { this._actionSubscribers .filter(function (sub) { return sub.before; }) .forEach(function (sub) { return sub.before(action, this$1.state); }); } catch (e) { { console.warn("[vuex] error in before action subscribers: "); console.error(e); } } var result = entry.length > 1 ? Promise.all(entry.map(function (handler) { return handler(payload); })) : entry[0](payload); //執行該action,如果大於1則用Promise.all() return result.then(function (res) { try { this$1._actionSubscribers .filter(function (sub) { return sub.after; }) .forEach(function (sub) { return sub.after(action, this$1.state); }); } catch (e) { { console.warn("[vuex] error in after action subscribers: "); console.error(e); } } return res }) };
最后返回的還是一個res,也就是Promise對象,這樣就實現了異步操作了。
