Vuex 3.x 框架原理分析


vuex 使用

main.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.store({
  state: { count: 0 },
  mutations: {
    increment(state) {
      state.count++;
    },
  },
  actions: {
    increment(context) {
      context.commit("increment");
    },
  },
});

new Vue({
  store,
  // ...
});

Vue.use 安装

install
export function install(_Vue) {
  if (Vue && _Vue === Vue) {
    if (__DEV__) {
      console.error(
        "[vuex] already installed. Vue.use(Vuex) should be called only once."
      );
    }
    return;
  }
  Vue = _Vue;
  applyMixin(Vue);
}

applyMixin通过全局 mixin 将$store 混入到所有 vue 组件中

applyMixin
export default function (Vue) {
  const version = Number(Vue.version.split(".")[0]);

  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit });
  } else {
    // 处理 vue 1.x的代码
    // 。。。
  }
  //  this = Vue实例
  // options.store
  function vuexInit() {
    const 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) {
      // 子组件从其父组件引用$store属性,层层嵌套进行设置
      this.$store = options.parent.$store;
    }
  }
}

Store 类

class Store
// 定义局部 Vue 变量,用于判断是否已经装载和减少全局作用域查找。
let Vue; // bind on install
export class Store {
  /*
     options =>  new Vuex.store(options)
     {state,getter,mutation,action}
    */
  constructor(options = {}) {
    // 若处于浏览器环境下且加载过Vue,则执行install方法。
    if (!Vue && typeof window !== "undefined" && window.Vue) {
      install(window.Vue);
    }
    // 用于判断是否 mutation 更改的state
    this._committing = false;
    //  action操作对象
    this._actions = Object.create(null);
    // 存放 action订阅
    this._actionSubscribers = [];
    // mutations操作对象
    this._mutations = Object.create(null);
    //  getter 操作对象
    this._wrappedGetters = Object.create(null);
    // 存放 modules 构建module树
    this._modules = new ModuleCollection(options);
    this._modulesNamespaceMap = Object.create(null);
    // 收集订阅者
    // https://v3.vuex.vuejs.org/zh/api/#watch
    this._subscribers = [];
    // Vue组件用于watch监视变化
    this._watcherVM = new Vue();
    this._makeLocalGettersCache = Object.create(null);

    const store = this;
    const { dispatch, commit } = this;
    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);
    };

    // strict mode
    this.strict = strict;

    // 获取 state
    const state = this._modules.root.state;

    // 安装 module
    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);

    // apply plugins
    plugins.forEach((plugin) => plugin(this));

    const useDevtools =
      options.devtools !== undefined ? options.devtools : Vue.config.devtools;
    if (useDevtools) {
      devtoolPlugin(this);
    }
  }
}

dispatch

this.dispatch = function boundDispatch(type, payload) {
  return dispatch.call(store, type, payload);
};
// type action中的方法
// payload 传递给action的参数
store.dispatch("increment", 10);
dispatch

  dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)

    const action = { type, payload }
    // 获取 当前 type下所有action处理函数的集合
    const entry = this._actions[type]
    this._actionSubscribers
        .slice()
        .filter(sub => sub.before)
        .forEach(sub => sub.before(action, this.state))


    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {

          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))

        resolve(res)
      })
    })
  }

commit

this.commit = function boundCommit(type, payload, options) {
  return commit.call(store, type, payload, options);
};
//  type mutations中的 type

store.commit("increment");
commit
commit (_type, _payload, _options) {
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options)

    const mutation = { type, payload }
    const entry = this._mutations[type]
    // 专用修改state方法,其他修改state方法均是非法修改
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    // 订阅者函数遍历执行,传入当前的mutation对象和当前的state
    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
  }

_withCommit (fn) {
     // 保存之前的提交状态
    const committing = this._committing
    // 进行本次提交,若不设置为true,直接修改state,strict模式下,Vuex将会产生非法修改state的警告
    this._committing = true
     // 执行state的修改操作
    fn()
    this._committing = committing
  }

module 安装

this._modules = new ModuleCollection(options);
// 获取 state
const state = this._modules.root.state;

// 安装 module
installModule(this, state, [], this._modules.root);

resetStoreVM(this, state);
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

收集模块构造模块树

ModuleCollection
export default class ModuleCollection {
  // rawRootModule = options
  constructor(rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false);
  }

  get(path) {
    //  this.root 为默认值
    return path.reduce((module, key) => {
      return module.getChild(key);
    }, this.root);
  }
  register(path, rawModule, runtime = true) {
    const newModule = new Module(rawModule, runtime);
    if (path.length === 0) {
      // 将root module 赋值给 root
      //  this.root.state = options.state
      this.root = newModule;
    } else {
      // -1 表示最后一个元素 (不包含最后一个元素)
      // 浅拷贝 path
      // 获取 父module
      const parent = this.get(path.slice(0, -1));
      //  将循环注册的子模块 添加到父模块的 _children对象中
      parent.addChild(path[path.length - 1], newModule);
    }

    // register nested modules
    // 注册子模块
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime);
      });
    }
  }
}
export default class Module {
  constructor(rawModule, runtime) {
    this.runtime = runtime;
    // Store some children item
    this._children = Object.create(null);
    // Store the origin module object which passed by programmer
    this._rawModule = rawModule;
    const rawState = rawModule.state;

    // Store the origin module's state
    this.state = (typeof rawState === "function" ? rawState() : rawState) || {};
  }
  getChild(key) {
    return this._children[key];
  }
  addChild(key, module) {
    this._children[key] = module;
  }
}

module 安装,注册对应的 state,mutations,actions,getters

installModule
//
function installModule(store, rootState, path, module, hot) {
  const isRoot = !path.length;
  const namespace = store._modules.getNamespace(path);

  // register in namespace map
  // 注册命名空间
  if (module.namespaced) {
    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(
        `[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join(
          "/"
        )}`
      );
    }
    store._modulesNamespaceMap[namespace] = module;
  }

  // set state
  // 不是跟主机设置 state方法
  // this.$store.a.state.stateA
  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1));
    const moduleName = path[path.length - 1];
    store._withCommit(() => {
      Vue.set(parentState, moduleName, module.state);
    });
  }

  const local = (module.context = makeLocalContext(store, namespace, path));
  // 注册对应模块的mutation,供state修改使用
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key;
    registerMutation(store, namespacedType, mutation, local);
  });
  //
  function registerMutation(store, type, handler, local) {
    const entry = store._mutations[type] || (store._mutations[type] = []);
    entry.push(function wrappedMutationHandler(payload) {
      handler.call(store, local.state, payload);
    });
  }
  // 注册对应模块的action,供数据操作、提交mutation等异步操作使用
  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key;
    const handler = action.handler || action;
    registerAction(store, type, handler, local);
  });

  function registerAction(store, type, handler, local) {
    const entry = store._actions[type] || (store._actions[type] = []);
    entry.push(function wrappedActionHandler(payload) {
      let res = handler.call(
        store,
        {
          dispatch: local.dispatch,
          commit: local.commit,
          getters: local.getters,
          state: local.state,
          rootGetters: store.getters,
          rootState: store.state,
        },
        payload
      );
      if (!isPromise(res)) {
        res = Promise.resolve(res);
      }
      if (store._devtoolHook) {
        return res.catch((err) => {
          store._devtoolHook.emit("vuex:error", err);
          throw err;
        });
      } else {
        return res;
      }
    });
  }
  // 注册对应模块的getters,供state读取使用
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key;
    registerGetter(store, namespacedType, getter, local);
  });
  function registerGetter(store, type, rawGetter, local) {
    if (store._wrappedGetters[type]) {
      return;
    }
    store._wrappedGetters[type] = function wrappedGetter(store) {
      return rawGetter(
        local.state, // local state
        local.getters, // local getters
        store.state, // root state
        store.getters // root getters
      );
    };
  }
  // 遍历子module 安装
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot);
  });
}

store 组件初始化

resetStoreVM
function resetStoreVM(store, state, hot) {
  const oldVm = store._vm;

  // bind store public getters
  store.getters = {};
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null);
  const wrappedGetters = store._wrappedGetters;
  const computed = {};
  // 选好遍历所有getter
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = partial(fn, store);
    Object.defineProperty(store.getters, key, {
      // this.$store.getters.xxxgetters 能够访问
      get: () => store._vm[key],
      //
      enumerable: true, // for local getters
    });
  });

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  const silent = Vue.config.silent;
  Vue.config.silent = true;
  // 设置新的storeVm,将当前初始化的state以及getters作为computed属性(刚刚遍历生成的)
  // Vuex其实构建的就是一个名为store的vm组件
  store._vm = new Vue({
    data: {
      $$state: state,
    },
    computed,
  });
  Vue.config.silent = silent;

  // enable strict mode for new vm
  if (store.strict) {
    // 该方法对state执行$watch以禁止从mutation外部修改state
    enableStrictMode(store);
    function enableStrictMode(store) {
      store._vm.$watch(
        "state",
        () => {
          assert(
            store._committing,
            `Do not mutate vuex store state outside mutation handlers.`
          );
        },
        { deep: true, sync: true }
      );
    }
  }

  // 若不是初始化过程执行的该方法,将旧的组件state设置为null,强制更新所有监听者(watchers),待更新生效,DOM更新完成后,执行vm组件的destroy方法进行销毁,减少内存的占用
  if (oldVm) {
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.$$state = null;
      });
    }
    Vue.nextTick(() => oldVm.$destroy());
  }
}

辅助函数

computed: mapState({
  // 箭头函数可使代码更简练
  count: (state) => state.count,

  // 传字符串参数 'count' 等同于 `state => state.count`
  countAlias: "count",

  // 为了能够使用 `this` 获取局部状态,必须使用常规函数
  countPlusLocalState(state) {
    return state.count + this.localCount;
  },
});
mapState
export const mapState = normalizeNamespace((namespace, states) => {
  const res = {};
  normalizeMap(states).forEach(({ key, val }) => {
    res[key] = function mappedState() {
      let state = this.$store.state;
      let getters = this.$store.getters;
      if (namespace) {
        const module = getModuleByNamespace(this.$store, "mapState", namespace);
        if (!module) {
          return;
        }
        state = module.context.state;
        getters = module.context.getters;
      }
      return typeof val === "function"
        ? val.call(this, state, getters)
        : state[val];
    };
    // mark vuex getter for devtools
    res[key].vuex = true;
  });
  return res;
});
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }

mapGetters
export const mapGetters = normalizeNamespace((namespace, getters) => {
  const res = {};
  normalizeMap(getters).forEach(({ key, val }) => {
    // The namespace has been mutated by normalizeNamespace
    val = namespace + val;
    res[key] = function mappedGetter() {
      if (
        namespace &&
        !getModuleByNamespace(this.$store, "mapGetters", namespace)
      ) {
        return;
      }
      return this.$store.getters[val];
    };
    // mark vuex getter for devtools
    res[key].vuex = true;
  });
  return res;
});

min-vux

let Vue;
class Store {
  constructor(options) {
    // 接收用户传入的参数
    this.$options = options;

    // 显式绑定this指向(dispatch中调用commit,commit的指向是undefined)
    this.commit = this.commit.bind(this);

    // 实现getters
    this.getters = {};
    let computed = {};
    var store = this;
    Object.keys(this.$options.getters).forEach((key) => {
      const fn = this.$options.getters[key];
      computed[key] = function () {
        return fn(store.state);
      };
      Object.defineProperty(this.getters, key, {
        get() {
          return store._vm[key];
        },
      });
    });

    // 响应式处理
    this._vm = new Vue({
      data() {
        return {
          $$state: options.state,
        };
      },
      computed,
    });
  }

  // 实现state
  get state() {
    return this._vm._data.$$state;
  }
  set state(val) {
    console.log("只能通过commit修改state的值");
    return;
  }

  // 实现commit
  commit(event, payload) {
    const fn = this.$options.mutations[event]; //找到对应mutations里面的函数
    if (!fn) {
      console.error("没有此mutation方法");
      return;
    } //判断有无此方法
    fn(this.state, payload); //传入state值,执行此方法
  }

  // 实现dispatch
  dispatch(event, payload) {
    const fn = this.$options.actions[event];
    if (!fn) {
      console.error("没有此mutation方法");
      return;
    }
    fn(this, payload);
  }
}

function install(_Vue) {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate() {
      // if判断是因为我们只在根实例里面传入了store
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store;
      }
    },
  });
}
export default {
  Store,
  install,
};
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.13.0