先贴一下store的构造函数
var Vue; // bind on install
// Store的构造类除了初始化一些内部变量以外,主要执行了installModule(初始化module)以及resetStoreVM(通过VM使store“响应式”)
var Store = function Store (options) {
debugger
var this$1 = this;
if ( options === void 0 ) options = {};
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
if (process.env.NODE_ENV !== 'production') {
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.");
}
var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];
var strict = options.strict; if ( strict === void 0 ) strict = false;
// store internal state
/* 用来判断严格模式下是否是用mutation修改state的 */
this._committing = false;
this._actions = Object.create(null);
this._actionSubscribers = [];
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
// 根据options 收集 modules
this._modules = new ModuleCollection(options);
this._modulesNamespaceMap = Object.create(null);
this._subscribers = [];
this._watcherVM = new Vue();
this._makeLocalGettersCache = Object.create(null);
// bind commit and dispatch to self
/*将dispatch与commit调用的this绑定为store对象本身,否则在组件内部this.dispatch时的this会指向组件的vm*/
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)
};
// strict mode
/*严格模式(使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误)*/
this.strict = strict;
var state = this._modules.root.state;
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
/*初始化根module,这也同时递归注册了所有子module,收集所有module的getter到_wrappedGetters中去,this._modules.root代表根module才独有保存的Module对象*/
installModule(this, state, [], this._modules.root);
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
/* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
resetStoreVM(this, state);
// apply plugins 调用插件
plugins.forEach(function (plugin) { return plugin(this$1); });
var useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools;
if (useDevtools) {
devtoolPlugin(this);
}
};
大概说一下我现在对store构造函数的理解:
1.初始化了一些变量,如_actions,_mutaions等
2.执行了installModule(初始化module)
3.执行了resetStoreVM(通过VM使store“响应式”)
先来看看第一步,有哪些可圈可点的逻辑线!
this._modules = new ModuleCollection(options);
// rawRootModule 就是你new Vuex.store()里传入的options
var ModuleCollection = function ModuleCollection (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false);
};
解读一下这个register方法
ModuleCollection.prototype.register = function register (path, rawModule, runtime) {
var this$1 = this;
if ( runtime === void 0 ) runtime = true;
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, rawModule);
}
var newModule = new Module(rawModule, runtime);
if (path.length === 0) {
this.root = newModule;
} else {
var parent = this.get(path.slice(0, -1));
parent.addChild(path[path.length - 1], newModule);
}
// register nested modules
if (rawModule.modules) {
forEachValue(rawModule.modules, function (rawChildModule, key) {
this$1.register(path.concat(key), rawChildModule, runtime);
});
}
这个方法里面,解读一下new Module这个方法
var newModule = new Module(rawModule, runtime);
var Module = function Module (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;
var rawState = rawModule.state;
// Store the origin module's state
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {};
};
所以,newModule这个对象的数据结构为
runtime: false
state: {sysmenu: null}
_children: {}
_rawModule: {state: {…}, mutations: {…}, actions: {…}, modules: {…}}
namespaced: (...)
__proto__: Object
其中_rawModule就是传入的options,继续回到store的构造函数里。这里生成的_modules在后面的installModule里会用到。
接着说store构造函数中第二点 installModule部分
function installModule (store, rootState, path, module, hot) {
var isRoot = !path.length;
/* 获取module的namespace */
var namespace = store._modules.getNamespace(path);
// register in namespace map
/* 如果有namespace则在_modulesNamespaceMap中注册 */
if (module.namespaced) {
if (store._modulesNamespaceMap[namespace] && process.env.NODE_ENV !== 'production') {
console.error(("[vuex] duplicate namespace " + namespace + " for the namespaced module " + (path.join('/'))));
}
store._modulesNamespaceMap[namespace] = module;
}
// set state
if (!isRoot && !hot) {
var parentState = getNestedState(rootState, path.slice(0, -1));
var moduleName = path[path.length - 1];
store._withCommit(function () {
if (process.env.NODE_ENV !== 'production') {
if (moduleName in parentState) {
console.warn(
("[vuex] state field \"" + moduleName + "\" was overridden by a module with the same name at \"" + (path.join('.')) + "\"")
);
}
}
Vue.set(parentState, moduleName, module.state);
});
}
var local = module.context = makeLocalContext(store, namespace, path);
module.forEachMutation(function (mutation, key) {
var namespacedType = namespace + key;
registerMutation(store, namespacedType, mutation, local);
});
module.forEachAction(function (action, key) {
var type = action.root ? key : namespace + key;
var handler = action.handler || action;
registerAction(store, type, handler, local);
});
module.forEachGetter(function (getter, key) {
var namespacedType = namespace + key;
registerGetter(store, namespacedType, getter, local);
});
module.forEachChild(function (child, key) {
installModule(store, rootState, path.concat(key), child, hot);
});
}
贴一下相关数据结构好理解。
在store的构造函数中有一系列这种样式的代码,拿一个当做例子解读作用,这里forEachAction中的这个function,其实被当做参数fn传递给了下面贴出来的源码
module.forEachAction(function (action, key) {
var type = action.root ? key : namespace + key;
var handler = action.handler || action;
registerAction(store, type, handler, local);
});
Module.prototype.forEachAction = function forEachAction (fn) {
// this._rawModule.actions 其实就是上面讲过的new Module里面生成的数据结构
if (this._rawModule.actions) {
forEachValue(this._rawModule.actions, fn);
}
};
这里实际上就是对obj对象获取keys,遍历keys,调用fn(),而这个fn是上面用到的
function forEachValue (obj, fn) {
Object.keys(obj).forEach(function (key) { return fn(obj[key], key); });
}
这里执行fn,实际上就是执行下面括号里面的方法
module.forEachAction(function (action, key) {
var type = action.root ? key : namespace + key;
var handler = action.handler || action;
registerAction(store, type, handler, local);
});
这里其实就是,判断actions是否有数据,如果有数据,就通过遍历,对每一个actions里面的数据调用上面的方法,即携带namespace参数,调用registerAction,结果就是在store上,_actions对象里有了数据。
执行完上述逻辑,初始化store时,传入的数据,包括有namespace的store都可以在_actions,_mutations中访问到了!
然后看第三个小点:resetStoreVM(this, state)
function resetStoreVM (store, state, hot) {
var oldVm = store._vm;
// bind store public getters
store.getters = {};
// reset local getters cache
store._makeLocalGettersCache = Object.create(null);
var wrappedGetters = store._wrappedGetters;
var computed = {};
forEachValue(wrappedGetters, function (fn, key) {
// use computed to leverage its lazy-caching mechanism
// direct inline function use will lead to closure preserving oldVm.
// using partial to return function with only arguments preserved in closure environment.
computed[key] = partial(fn, store);
Object.defineProperty(store.getters, key, {
get: function () { return 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
var silent = Vue.config.silent;
Vue.config.silent = true;
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
Vue.config.silent = silent;
// enable strict mode for new vm
if (store.strict) {
enableStrictMode(store);
}
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(function () {
oldVm._data.$$state = null;
});
}
Vue.nextTick(function () { return oldVm.$destroy(); });
}
}
在这个方法里,两个核心:一个是将getters变成计算属性,resetStoreVM首先会遍历wrappedGetters,使用Object.defineProperty方法为每一个getter绑定上get方法,这样我们就可以在组件里访问this.$store.getters.test就等同于访问store._vm.test。另一个:new一个Vue对象来实现数据的“响应式化”,运用Vue.js内部提供的数据双向绑定功能来实现store的数据与视图的同步更新。