总体思路:
1.将用户传入的数据进行格式化操作(树结构)
2. 递归安装模块
具体:
1.使用vuex时,会使用Vue.use(vuex).
use方法默认会调用vuex的install方法,并且会将Vue传入。
这样的好处是在vuex中不需要依赖vue的包。
install方法中,对vue生命周期做一次混合,将store放在每一个实例上。
function install(_vue) {
Vue = _vue; Vue.mixin({
beforeCreate() {
if (this.$options.store) {
this.$store = this.$options.store;
} else {
this.$store = this.$parent && this.$parent.$store;
}
}
});
}2. 用户调用new vuex.Store时,创建实例化Store(new vuex.Store)。实例化时会拿到用户传入的选项options--->class Store
获取到用户选项后,使用new Vue。目的是使数据实现响应式的变化,比如数据更新了视图可以刷新。-->
this.vm = new Vue({
data() {
return {
state: options.state
}
}
}); 核心思想是将用户传入的数据定义到actions、mutations、getters、state上。这样用户可以通过全局的状态去更新页面。
this.getters = {};
this.mutations = {};
this.actions = {}; vuex可以传入modules,所以需要将用户传入的数据格式化。(vuex中的递归都是通过队列进行递归,这样可以清晰的知道父子层级关系)
this.modules = new ModulesConnect(options);//将用户传入的数据进行格式化操作默认将用户传入的对象格式一个包含_raw(用户传入的原有对象)、_children(孩子)、state.将每个模块的state单独提出来,目的是为了后续安装方便。并在用户传入的模块中将格式化的结果记录下来,方便动态添加模块时安装。
默认作用于根模块,如果当前模块中有modules时,循环这个模块,继续注册它的子模块。
注册子模块时主要是依靠模块名将其拼在path中,继续注册,此时需要知道子模块的父级是谁,通过path.slice(0,-1),看其是否有父亲,如果没有父亲,将其定义在根模块的孩子中。如果有父亲,将其定义在父亲模块的_children中。这样就可以格式化一个树形结构中,
然后递归的安装模块,将刚格式化的数据。定义在Store上的actions、getters、mutations上。有儿子时会深度遍历。
function installModules(store, rootState, path, rawModules)特殊的是state状态。子模块的状态需要使用vue.set方法。
if (path.length > 0) { //子模块
let parentState = path.slice(0, -1).reduce((root, current) => {
return rootState[current];
}, rootState);
Vue.set(parentState, path[path.length - 1], rawModules.state);
} 动态添加模块时,判断传入的模块名是否是数组,如果不是数组则格式化成数组。添加到刚格式化的树结构中,然后进行注册。
function forEach(objs, callback) {
Object.keys(objs).forEach(name => {
return callback(name, objs[name]);
})
}
class Store {
constructor(options) {
this.vm = new Vue({
data() {
return {
state: options.state
}
}
});
this.getters = {};
this.mutations = {};
this.actions = {};
this.modules = new ModulesConnect(options);//将用户传入的数据进行格式化操作
installModules(this, this.state, [], this.modules.root);// 递归安装模块
}
get state() {
return this.vm.state;
}
commit = (mutationName, payload) => {
this.mutations[mutationName].forEach(mutation=>{
mutation(payload);
})
}
dispatch = (actionName, payload) => {
this.actions[actionName].forEach(action=>{
action(payload);
});
}
registerModule(moudleName,module){ //动态添加组件
if(!Array.isArray(moudleName)){
moudleName = [moudleName];
}
this.modules.register(moudleName,module);
installModules(this,this.state,moudleName,module.rowModules)
}
}
function installModules(store, rootState, path, rawModules) {
let getters = rawModules._row.getters;
let actions = rawModules._row.actions;
let mutations = rawModules._row.mutations;
if (path.length > 0) {
let parentState = path.slice(0, -1).reduce((root, current) => {
return rootState[current];
}, rootState);
Vue.set(parentState, path[path.length - 1], rawModules.state);
}
if (getters) {
forEach(getters, (getterName, value) => {
Object.defineProperty(store.getters, getterName, {
get: () => {
value(store.state);
}
})
})
}
if (actions) {
forEach(actions, (actionName, value) => {
let arr = store.actions[actionName] || (store.actions[actionName] = []);
arr.push((payload) => {
value(store, payload);
})
});
}
if (mutations) {
forEach(mutations, (mutationName, value) => {
let arr = store.mutations[mutationName] || (store.mutations[mutationName] = []);
arr.push((payload) => {
value(store.state, payload);
})
});
}
if (rawModules.children) {
forEach(rawModules.children, (moudleName, modules) => {
installModules(store, rootState, path.concat(moudleName), modules);
});
}
}
class ModulesConnect {
constructor(options) {
this.register([], options);
}
register(path, rootModules) {
let rowModules = {
_row: rootModules,
children: {},
state: rootModules.state
}
rootModules.rowModules = rowModules;
if (!this.root) {
this.root = rowModules;
} else { //[a] [b] [b c]
let parentMoudles = path.slice(0, -1).reduce((root, current) => {
return root.children[current];
}, this.root);
parentMoudles.children[path[path.length - 1]] = rowModules;
} i
if (rootModules.modules) {
forEach(rootModules.modules, (moudleName, modules) => {
this.register(path.concat(moudleName), modules);
});
}
}
}