• 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对象,这样就实现了异步操作了。

  • 相关阅读:
    CSS让DIV按照背景图片的比例缩放,并让背景图片填充整个元素(转)
    判断浏览器
    $.each遍历json对象
    jq塞入不同状态html的写法 switch (defaults.type)
    vue资料
    第三方登录
    获取一个项目的所有接口
    接口工具调研
    go自动化测试平台
    压测工具 vegeta
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11424516.html
Copyright © 2020-2023  润新知