vuex 源碼分析(五) action 詳解


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對象,這樣就實現了異步操作了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM