• Vuex 工作原理


    环境和测试代码

    vue 2.6.11, vuex 3.1.3

    <!DOCTYPE html>
    <html>
    <head>
      <title>vue test</title>
    </head>
    <body>
    <div id="app">
      <button @click="doAdd">do add 2</button>
      {{vCount}}
      <button-counter></button-counter>
    </div>
    
      <!-- Vue.js v2.6.11 -->
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script src="https://cdn.bootcss.com/vuex/3.1.3/vuex.js"></script>
      <script>
        Vue.component('button-counter', {
          data: function () {
            return {}
          },
          mounted() {
            console.log('child ', this.$store);
          },
          template: '<button v-on:click="doChildAdd">do add 5</button>',
          methods: {
            doChildAdd() {
              store.commit('increment', 5)
            }
          }
        });
        const store = new Vuex.Store({
          state: {
            count: 1
          },
          mutations: {
            increment (state, n = 1) {
              state.count += n;
            }
          }
        })
        var app = new Vue({
          el: '#app',
          store,
          computed: {
            vCount () {
              return this.$store.state.count
            },
          },
          mounted() {
            console.log('parent ', this.$store);
          },
          methods: {
            doAdd() {
              store.commit('increment', 2)
            }
          }
        })
    
        console.log(app);
        // var event = new CustomEvent('test', { 'detail': 5 }); window.dispatchEvent(event);
      </script>
    </body>
    </html>
    

    总结

    侵入每个 vue 组件注册了 $store 属性,而所有 $store 属性都指向一个 store 实例,这样就能做到所有 vue 组件访问的都是同一份全局变量。

    vue 组件里用户定义取 store 上的变量用于渲染或者其他逻辑,而后改动 this.store.xxx 时,vue 本身核心依赖收集能知道要更新哪些视图,就完成了。

    分析

    首先给出 store 的定义代码,注意下文的 installModule 和 resetStoreVM,这是两个关键的步骤

    var Store = function Store (options) {
      // ...
    
      this._actions = Object.create(null);
      this._actionSubscribers = [];
      this._mutations = Object.create(null);
      this._wrappedGetters = Object.create(null);
      this._modules = new ModuleCollection(options);
      this._modulesNamespaceMap = Object.create(null);
      this._subscribers = [];
    
    
      // bind commit and dispatch to self
      var store = this;
      var ref = this;
      var dispatch = ref.dispatch;
      var commit = ref.commit;
      this.dispatch = function boundDispatch (type, payload) {
        return dispatch.call(store, type, payload)
      };
      this.commit = function boundCommit (type, payload, options) {
        return commit.call(store, type, payload, options)
      };
      
      var state = this._modules.root.state;
    
      // init root module.
      // this also recursively registers all sub-modules
      // and collects all module getters inside this._wrappedGetters
      installModule(this, state, [], this._modules.root);
    
      // initialize the store vm, which is responsible for the reactivity
      // (also registers _wrappedGetters as computed properties)
      resetStoreVM(this, state);
    
      // ...
    };
    

    resetStoreVM(this, state) 方法

    store._vm = new Vue({
      data: {
        $$state: state
      },
      computed: computed
    });
    

    install(window.Vue); 方法是用来挂载 store 对象到 vue 实例的,里面做了件事 Vue.mixin({ beforeCreate: vuexInit });

    vuexInit 函数里就做了把 store 复制给 vue 组件的 this.$store 属性.

    function vuexInit () {
      var options = this.$options;
      // store injection
      if (options.store) {
        this.$store = typeof options.store === 'function'
          ? options.store()
          : options.store;
      } else if (options.parent && options.parent.$store) {
        this.$store = options.parent.$store;
      }
    }
    

    commit

    下文关键部分是 this._withCommit 里的匿名函数,它遍历 entry,执行用户自定义的 handler 处理函数,

    就上文的测试代码来说,这个 handler 就是 commit 里的函数 increment (state, n = 1) { state.count += n; }

    接着 handler 里要读取 state.count,就会去获取 state,return this._vm._data.$$state

    Store.prototype.commit = function commit(_type, _payload, _options) {
      var this$1 = this;
    
      // check object-style commit
      var ref = unifyObjectStyle(_type, _payload, _options);
      var type = ref.type;
      var payload = ref.payload;
      var options = ref.options;
    
      var mutation = { type: type, payload: payload };
      var entry = this._mutations[type];
      
      this._withCommit(function() {
        entry.forEach(function commitIterator(handler) {
          handler(payload);
        });
      });
    
      // ...
    };
    
    // _withCommit 执行它所传入的 fn,它遍历 entry,执行用户自定义的 handler 处理函数,
    // 这个 handler 就是我们定义的 commit 里的函数 increment (state, n = 1) { state.count += n; },总之要变动 state.count,就会进入 state 的拦截器, 
    
    prototypeAccessors$1.state.get = function () {
      return this._vm._data.$$state
    };
    
    // 一旦触发去 vue 的 _data 上有 vue 自己的拦截器 get,而动作做 state.count += n 后,就触发了 vue 自己拦截器里的 set。最后这样就开始vue自身的渲染逻辑。
    

    最后修改了 state.count 后,等于说用户也变动了在 vue 组件里那个 computed,自然而然的进入 vue 组件自身的 get/set 以及渲染逻辑。

    computed: {
      vCount () {
        return this.$store.state.count
      },
    },
    

    dispatch

    dispatch 与 commit 的流程大体相同,不同点是他会使用 Promise.all 来保证 handler 函数的异步触发,并且最后也会 return 一个 promise 对象出去而已。

    Store.prototype.dispatch = function dispatch(_type, _payload) {
      var this$1 = this;
    
      // check object-style dispatch
      var ref = unifyObjectStyle(_type, _payload);
      var type = ref.type;
      var payload = ref.payload;
    
      var action = { type: type, payload: payload };
      var entry = this._actions[type];
    
        entry.length > 1
          ? Promise.all(
              entry.map(function(handler) {
                return handler(payload);
              })
            )
          : entry[0](payload);
    
      return result.then(function(res) {
        return res;
      });
    };
    
    
  • 相关阅读:
    九度OJ 1168:字符串的查找删除 (查找)
    九度OJ 1167:数组排序 (排序)
    九度OJ 1166:迭代求立方根 (迭代)
    九度OJ 1165:字符串匹配 (模式匹配)
    九度OJ 1164:旋转矩阵 (矩阵运算)
    九度OJ 1163:素数 (素数)
    九度OJ 1162:I Wanna Go Home(我想回家) (最短路径)
    九度OJ 1161:Repeater(复制器) (递归)
    九度OJ 1160:放苹果 (DFS)
    Sub-process /usr/bin/dpkg returned an error code (1)错误解决办法
  • 原文地址:https://www.cnblogs.com/everlose/p/12564535.html
Copyright © 2020-2023  润新知