vuex3源码注释系列 /src/store.js

import applyMixin from "./mixin";
import devtoolPlugin from "./plugins/devtool";
import ModuleCollection from "./module/module-collection";
import {
    forEachValue, isObject, isPromise, assert, partial } from "./util";

let Vue; // bind on install

/***
 * Vuex 使用的第二步,即 new Vuex.Store( options ) 的数据初始化过程。
 * 1、使用 _modulesNamespaceMap 来收集所有创建的 module 实例。
 * 2、使用 rootState(store._modules.root.state)来收集所有 module 实例的 state 数据。
 *
 *  _committing: 用于判断是不是通过commit方式执行。如果mutation是异步方式执行,则 _committing 已经为 false 了。
 * 
 *
 * 关于 local 对象:
 * ===> local 表示查找“对应module”的dispatch,commit,getter,state;
 * ===> store 表示查找”root module“的dispatch,commit,getter,state;
 * 1、local 对象相当于 对应module实例的context对象,用于查找对应module的 dispatch,commit,getters,state。
 * 2、用户自定义的 action 方法第一个参数携带的参数为:
 *    {
        dispatch: local.dispatch,
        commit: local.commit,
        getters: local.getters,
        state: local.state,
        rootGetters: store.getters,
        rootState: store.state,
      },
 * 3、用户自定义的 mutation 方法第一个参数携带的参数为:
 * 4、用户自定义的 getter 方法有四个参数,分别是:
 *    (  
 *      local.state, // local state
        local.getters, // local getters
        store.state, // root state
        store.getters // root getters 
      )
 */
export class Store {
   
  /**
   * 关于 Object.create(null)
   * ===> Object.create(null)没有继承任何原型方法,也就是说它的原型链没有上一层。
   * ===> 相关分析文章: https://juejin.cn/post/6844903589815517192
   */

  constructor(options = {
   }) {
   
    // 条件1: !Vue  ==> 如果创建 Vuex.Store() 对象时,没有经过 Vue.use(Vuex)将 Vue 实例保存。
    // 条件2: typeof window !== 'undefined' 表示是浏览器环境。
    // 条件3: window.Vue 表示 window 上有挂在 vue 实例。
    if (!Vue && typeof window !== "undefined" && window.Vue) {
   
      //那么就使用 window.Vue 来走 vuex.install 的挂载流程。
      install(window.Vue);
    }

    /**
     * __DEV__ 不是一个真实存在的变量。它在JS代码编译阶段,会被一个常量来替换,通常在 development 下是 true,在 production 模式下是 false
     */
    if (__DEV__) {
   
      assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`);
      assert(
        typeof Promise !== "undefined",
        `vuex requires a Promise polyfill in this browser.`
      );
      assert(
        this instanceof Store,
        `store must be called with the new operator.`
      );
    }

    /**
     * 从 options 中获取 plugins 插件数组, strict 严格模式属性。
     */
    const {
    plugins = [], strict = false } = options;

    //用来标记是否通过 commit(xxxx, payload) 方式来出发的 state 状态的更新。
    //this._committing 是 store 中的一个内部变量。
    this._committing = false;

    //创建一个 _actions 对象。
    this._actions = Object.create(null);
    this._actionSubscribers = [];

    //创建一个 _mutations 对象。
    this._mutations = Object.create(null);
    //创建一个 _wrappedGetters 对象。

    this._wrappedGetters = Object.create(null);
    //创建 modules 对象。
    /***
     * 1、此时已经将options数据转换成了 module 实例结果。
     *    moduleCollection 结构为: {
     *        root: rootModule
     *    }
     *    module实例结构为: {
     *        runtime: runtime,
     *        state: rawModule.state
     *        _children: {
     *          user: {
     *            runtime: runtime,
     *            state:   对应rawModule.state,
     *            __children: {
     *              xxxxxx
     *            }
     *          },
     *          info: {
     *            runtime: runtime,
     *            state:   对应rawModule.state,
     *            __children: {
     *              xxxxxx
     *            }
     *          }
     *        }
     *    }
     * __modules 指向 moduleCollection 实例。
     */
    this._modules = new ModuleCollection(options);
    //常见命名空间(namespace)map。这个是用来存储已经创建的 module 实例的。
    this._modulesNamespaceMap = Object.create(null);

    //创建订阅者队列。
    this._subscribers = [];
    //vue 实例。但是没有设置数据源,借用vue的监听
    this._watcherVM = new Vue();
    /**
     * _wrappedGetters
     * _makeLocalGettersCache
     */
    //创建一个 Getters 的缓存
    this._makeLocalGettersCache = Object.create(null);

    //在下面的函数中,函数作用域中的 this 并不指向 store, 所以用了个 store 对象指向本身。 类似于 let that = this;
    const store = this;
    //获取 Store 类中的 dispath, commit 方法。
    const {
    dispatch, commit } = this;
    //对 store 本身的 dispatch 方法进行装饰增强。主要用于保证 dispatch 内的 this,永远是指向 store。
    this.dispatch = function boundDispatch(type, payload) {
   
      return dispatch.call(store, type, payload);
    };
    //对 store 本身的 commit 方法进行装饰增强。主要用于保证 commit 内的 this,永远是指向 store。
    this.commit = function boundCommit(type, payload, options) {
   
      return commit.call(store, type, payload, options);
    };

    //获取严格模式,默认是 false。
    // 可以在 new Vuex.Store( { strict: true } ) 方式来设置
    this.strict = strict;

    //获取根 modules 上的 state 对象。
    const state = this._modules.root.state;

    /**
     * 递归的形式,将 root module,以及所有的 children module 进行初始化和注册。
     * 1、初始化 root module。
     * 2、递归注册所有的 children modules。
     * 3、收集所有 modules 的 getters 放到 this._wrappedGetters 中。
     */
    installModule(this, state, [], this._modules.root);

    /**
     * 初始化 store vm 响应式对象。同时注册 __wrappedGetters 为 vm 的计算属性。
     */
    resetStoreVM(this, state);

    //应用 Vuex.Store( { plugins: [ xxxx, xxx ] } ) 中注册的插件。
    plugins.forEach((plugin) => plugin(this));

    //判断是否使用 devtool.
    //除了 devtools 插件会被 vuex 内置判断进行使用。其余的插件都需要在 new Vuex.Store(options) 中 { plugins: [xxx] } 形式配置。
    const useDevtools =
      options.devtools !== undefined ? options.devtools : Vue.config.devtools;
    if (useDevtools) {
   
      devtoolPlugin(this);
    }
  }

  /**
   * store.state的取值
   */
  get state() {
   
    // resetStoreVM() 方法时,会创建 vue 实例,且会把 state 作为 vue中data的属性;
    //   会把 getters 转换为 computed。
    return this._vm._data.$$state;
  }

  /**
   * 能通过 $store.state 获取属性,但是不能对 $store.state 设置值。
   */
  set state(v) {
   
    if (__DEV__) {
   
      assert(
        false,
        `use store.replaceState() to explicit replace store state.`
      );
    }
  }

  /**
   * 被外部调用的 commit 方法。 this.$store.commit( "/user/info/setName", { ... }, options );
   *    第一个参数: store的 namespace 对应的值,用来在 store._mutation 中取 wrappedMutationHandler 方法。
   *    第二个参数: payload 表示携带的数据。
   *    第三个参数: options 已经不再使用。
   *
   * 其中 _type 可以是字符串,也可以是对象。
   *    如果是字符串,则形式为: "/user/infi/setName";
   *    如果是不为null的对象,则去 _type.type 作为 commit 的第一个参数, _type 自身作为第二个参数, payload 作为第三个参数。
   *
   * 1、mutation 对应的订阅队列为: _subscribers。
   * 2、同一个 key, 可以存在多个 mutation 方法。
   */
  commit(_type, _payload, _options) {
   
    //检查_type的数据类型,如果是字符串,则什么都不处理。
    //   如果是对象,且存在 type.type,则将 type.type 作为type。type转为 payload, payload转为 options。
    const {
    type, payload, options } = unifyObjectStyle(
      _type,
      _payload,
      _options
    );

    //type就是命名空间名称, payload 携带的数据。
    const mutation = {
    type, payload };
    //store中通过 installModule 存储的 mutation。 entry 是一个数组,允许相同key,存储多个 mutation 方法。
    const entry = this._mutations[type];
    //当 options 中不存在一个 mutaions 时,则 entry 不会被初始化为 [].
    if (!entry) {
   
      //如果开发环境下,则报红。
      if (__DEV__) {
   
        console.error(`[vuex] unknown mutation type: ${
     type}`);
      }
      return;
    }

    //this._withCommit 主要是提供一个 committing 的环境。用于判断 state 中的属性值,是否是通过 this.$store.commit() 的方式进行更改。
    this._withCommit(() => {
   
      //遍历执行当前 type 对应的所有 commit 方法。
      entry.forEach(function commitIterator(handler) {
   
        //定义执行 用户定义的 commit 的包装方法 function wrappedMutationHandler(payload) {}
        //   而在 wrappedMutationHandler 会强制让 this 绑定为 store, 且多传入一个当前 module对一个的 state 对象。
        handler(payload);
      });
    });

    //store中的发布订阅模式下的 订阅函数,会被调用。
    //   第一个参数:mutation 是 { type, payload } 数据对象。
    //   第二个参数是 this._vm.$$data.state。
    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach((sub) => sub(mutation, this.state));

    //明显在 commit 中传递 options 参数,没有被使用了。
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值