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 参数,没有被使用了。
vuex3源码注释系列 /src/store.js
于 2022-03-16 20:17:56 首次发布