import vuex 的初始化:安装与 Store 实例化 安装 通过 mixin 的方式在组件 beforeCreate 生命周期的时候往组件注册$store Store 实例化 import Vuex from "vuex" 会实例化 Store 对象,并且返回 store 实例,传入 new Vue 的 options 中,并且往 Store 的构造函数传入 actio…

import vuex 的初始化:安装与 Store 实例化

安装

通过 mixin 的方式在组件 beforeCreate 生命周期的时候往组件注册$store

// src/index.js
import { Store, install } from "./store";
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from "./helpers";
export default {
  Store,
  install,
  version: "__VERSION__",
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
};
// src/store.js
// 把传入的_Vue赋给Vue,并执行applyMixin方法
export function install(_Vue) {
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== "production") {
      console.error("[vuex] already installed. Vue.use(Vuex) should be called only once.");
    }
    return;
  }
  // 把传入的_Vue赋给Vue
  Vue = _Vue;
  // 并执行applyMixin方法
  applyMixin(Vue);
}
// src/mixin.js
// 核心方法:通过mixin的方式在组件beforeCreate生命周期的时候往组件注册$store
export default function (Vue) {
  const version = Number(Vue.version.split('.')[0])
  if (version >= 2) {
    // 2.x版本
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // 兼容1.x版本
  }
  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) {
      this.$store = options.parent.$store
    }
  }
}

Store 实例化

  1. import Vuex from "vuex" 会实例化 Store 对象,并且返回 store 实例,传入 new Vue 的 options 中,并且往 Store 的构造函数传入 actions、getters、state、muations、modules
  2. vuex 为了保证 store 不会随着应用的变大而变得臃肿,提出了模块的概念:把 store 分隔成 module,每个 module 允许有自己的 state、mutation,action,getter和childmodule,store是一个根数据module,大module中有小module,为了让module是一个独立的部分,每个module有自己的命名空间
  3. 所以 store 的实例化分为三部分:一、初始化模块:收集 module 的构造函数 二、安装模块 三、初始化 store.\_vm
// src/store.js
// Store的构造函数
export class Store {
  constructor(options = {}) {
    const { plugins = [], strict = false } = options;
    // 初试化依赖的参数
    this._committing = false;
    this._actions = Object.create(null);
    this._actionSubscribers = [];
    this._mutations = Object.create(null);
    this._wrappedGetters = Object.create(null);
    // 一、初始化模块:收集module的构造函数
    this._modules = new ModuleCollection(options);
    this._modulesNamespaceMap = Object.create(null);
    this._subscribers = [];
    this._watcherVM = new Vue();
    // 绑定commit 和 dispatch 信号机制
    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);
    };
    // 是否开始严格模式
    this.strict = strict;
    const state = this._modules.root.state;
    // 二、安装模块
    installModule(this, state, [], this._modules.root);
    // 三、初始化 store.\_vm
    resetStoreVM(this, state);
    // 导入插件
    plugins.forEach((plugin) => plugin(this));
    if (Vue.config.devtools) {
      devtoolPlugin(this);
    }
  }
}
  • 一、初始化模块:收集 module 的构造函数
// 初始化模块:收集module的构造函数
// src/module/module-collection.js
export default class ModuleCollection {
  constructor(rawRootModule) {
    // 一、首先是注册module,传入参数:路径,模块的原始配置,是否在运行的时候创建模块
    this.register([], rawRootModule, false);
  }
  // 根据路径获取到父模块
  get(path) {
    return path.reduce((module, key) => {
      return module.getChild(key);
    }, this.root);
  }

  getNamespace(path) {
    let module = this.root;
    return path.reduce((namespace, key) => {
      module = module.getChild(key);
      return namespace + (module.namespaced ? key + "/" : "");
    }, "");
  }

  update(rawRootModule) {
    update([], this.root, rawRootModule);
  }
  // 注册
  register(path, rawModule, runtime = true) {
    // 1、创建一个Module实例
    const newModule = new Module(rawModule, runtime);
    if (path.length === 0) {
      // 如果store树的长度为0,表明是一个根模块
      this.root = newModule;
    } else {
      // 如果store树的长度不为0,建立module的父子关系:1)根据路径获取到父模块 2)调用addChild建立父子关系
      const parent = this.get(path.slice(0, -1));
      parent.addChild(path[path.length - 1], newModule);
    }
    // 2、遍历当前模块中定义的所有modules,遍历的时候当前索引作为注册module的path,然后递归调用注册
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime);
      });
    }
  }
  // 卸载module
  unregister(path) {
    const parent = this.get(path.slice(0, -1));
    const key = path[path.length - 1];
    if (!parent.getChild(key).runtime) return;
    parent.removeChild(key);
  }
}
// src/module/module.js
export default class Module {
  constructor (rawModule, runtime) {
    // 是否在运行时候创建
    this.runtime = runtime
    // 所有的子模块,默认是一个空对象
    this._children = Object.create(null)
    // 这个是模块的配置参数
    this._rawModule = rawModule
    const rawState = rawModule.state

    // 当前模块定义的state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
  get namespaced () {
    return !!this._rawModule.namespaced
  }
  addChild (key, module) {
    this._children[key] = module
  }
  removeChild (key) {
    delete this._children[key]
  }
  getChild (key) {
    return this._children[key]
  }
  update (rawModule) {
    this._rawModule.namespaced = rawModule.namespaced
    if (rawModule.actions) {
      this._rawModule.actions = rawModule.actions
    }
    if (rawModule.mutations) {
      this._rawModule.mutations = rawModule.mutations
    }
    if (rawModule.getters) {
      this._rawModule.getters = rawModule.getters
    }
  }
  forEachChild (fn) {
    forEachValue(this._children, fn)
  }
  forEachGetter (fn) {
    if (this._rawModule.getters) {
      forEachValue(this._rawModule.getters, fn)
    }
  }
  forEachAction (fn) {
    if (this._rawModule.actions) {
      forEachValue(this._rawModule.actions, fn)
    }
  }
  forEachMutation (fn) {
    if (this._rawModule.mutations) {
      forEachValue(this._rawModule.mutations, fn)
    }
  }
}
  • 二、安装模块
// src/store.js
// 5个参数:store是root store,rootState是root state,path是模块访问路径,module是当前模块,hot是是否热更新
function installModule(store, rootState, path, module, hot) {
  const isRoot = !path.length;
  // 获取命名空间
  const namespace = store._modules.getNamespace(path);
  // 1、把命名空间对应的模块缓存下载
  if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module;
  }
  // 2、如果不是root module,并且不是热更新,做一些处理
  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);
    });
  }
  // 3、根据目标模块的命名空间,构建一个本地的上下文环境
  const local = (module.context = makeLocalContext(store, namespace, path));
  // 遍历模块中定义的mutation,action,getter,执行注册
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key;
    // 给root store上的_mutations[types]添加wrappedMutationHandler方法
    registerMutation(store, namespacedType, mutation, local);
  });
  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key;
    const handler = action.handler || action;
    // 给root store上的_actions[types]添加wrappedActionHandler方法
    registerAction(store, type, handler, local);
  });
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key;
    // 给root store上的_wrappedGetters[key]添加wrappedGetter方法
    registerGetter(store, namespacedType, getter, local);
  });
  // 遍历所有module,递归执行installModule
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot);
  });
}
// src/module/module-collection.js
// 通过reduce方法,拼接命名空间
function getNamespace(path) {
  let module = this.root;
  return path.reduce((namespace, key) => {
    module = module.getChild(key);
    return namespace + (module.namespaced ? key + "/" : "");
  }, "");
}
// src/store.js
// 构建一个本地的上下文环境
// 对于getters、dispatch和commit来说,如果没有命名空间,说明没有子module,直接指向root store的getters、dispatch和commit
// 如果dispatch和commit有命名空间,在type上加上namespace,执行store对应方法
// 如果getters有命名空间,则返回makeLocalGetters方法的返回值
// state是从root state中开始,一层层查找子模块的state,直到直到目标模块的state
function makeLocalContext(store, namespace, path) {
  const noNamespace = namespace === "";
  const local = {
    dispatch: noNamespace
      ? store.dispatch
      : (_type, _payload, _options) => {
          const args = unifyObjectStyle(_type, _payload, _options);
          const { payload, options } = args;
          let { type } = args;

          if (!options || !options.root) {
            type = namespace + type;
            if (process.env.NODE_ENV !== "production" && !store._actions[type]) {
              console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`);
              return;
            }
          }

          return store.dispatch(type, payload);
        },
    commit: noNamespace
      ? store.commit
      : (_type, _payload, _options) => {
          const args = unifyObjectStyle(_type, _payload, _options);
          const { payload, options } = args;
          let { type } = args;

          if (!options || !options.root) {
            type = namespace + type;
            if (process.env.NODE_ENV !== "production" && !store._mutations[type]) {
              console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`);
              return;
            }
          }

          store.commit(type, payload, options);
        }
  };
  Object.defineProperties(local, {
    getters: {
      get: noNamespace ? () => store.getters : () => makeLocalGetters(store, namespace)
    },
    state: {
      get: () => getNestedState(store.state, path)
    }
  });
  return local;
}
// src/store.js
// 获取命名空间的长度,遍历root store下所有getters,判断是否匹配namespace,最后获取localTypes,localTypes实际上就是store.getters[type],获取root store下指定namespace的getter
function makeLocalGetters(store, namespace) {
  const gettersProxy = {};
  const splitPos = namespace.length;
  Object.keys(store.getters).forEach((type) => {
    if (type.slice(0, splitPos) !== namespace) return;
    const localType = type.slice(splitPos);
    Object.defineProperty(gettersProxy, localType, {
      get: () => store.getters[type],
      enumerable: true
    });
  });
  return gettersProxy;
}
// src/store.js
// state是从root state中开始,一层层查找子模块的state,直到直到目标模块的state
function getNestedState(state, path) {
  return path.length ? path.reduce((state, key) => state[key], state) : state;
}
  • 三、初始化 store._vm
// src/store.js
// 作用是建立 getters 和 state 之间的关系,getters可以获取state值,获取state值的时候可以做一些操作,只有state值有变化getters才会重新计算,本质上是用了vue的computed计算属性实现
// 在严格模式下会有一个watcher观测this._data的变化,当直接修改state时,会报警告
function resetStoreVM(store, state, hot) {
  const oldVm = store._vm;

  // bind store public getters
  store.getters = {};
  const wrappedGetters = store._wrappedGetters;
  const computed = {};
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = () => fn(store);
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    });
  });

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

  // 在严格模式下会有一个watcher观测this._data的变化,当直接修改state时,会报警告
  if (store.strict) {
    store._vm.$watch(
      function() {
        return this._data.$$state;
      },
      () => {
        if (process.env.NODE_ENV !== "production") {
          assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`);
        }
      },
      { deep: true, sync: true }
    );
  }

  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());
  }
}

暂无评论