本文参考珠峰架构公开课之
vuex
实现原理
之前的文章谈到了module
的数据的注册,接下来就是基于注册数据,执行installModule
函数安装。
installModule
函数的作用
先来看下平时vuex
怎么调用这些模块:
//获取state
this.$store.state.state_country
//获取getters
this.$store.getters.getter_country
//设置mutations
this.$store.commit("mutation_country","中国")
//设置actions
this.$store.dispatch("action_country",{})
组件开发中是通过this.$store
来操作state
,getters
,mutations
和actions
,所以在Store
类中存在state
,getters
两个属性,同时存在commit
和dispatch
两个方法分别用来触发mutation
和action
,先看下Store
类具体做了什么?
class Store {
constructor(options) {
this.vm = new Vue({
data() {
return {
temp_data: options.state
};
}
});
//将options传入的数据进行格式化
this.modules = new ModuleCollection(options);
this.getters = {};
this.mutations = {};
this.actions = {};
installModule(this, this.state, [], this.modules.root);
}
get state() {
return this.vm.temp_data;
}
commit = (mutationName, payload) => {
this.mutations[mutationName].forEach(fn => fn(mutationName, payload));
};
dispatch = (actionName, payload) => {
this.actions[actionName].forEach(fn => fn(actionName, payload));
};
}
Store
类主要做了以下三件事:
- 除了将
options
传入ModuleCollection
函数做了数据格式化。 - 以及通过实例化
Vue
对象将state
数据作为Vue
的属性实现双向绑定。 - 还新增了
getters
,mutations
和actions
三个属性。然后通过installModule
函数将各子模块的getters
,mutations
和actions
整合到Store
对应的属性上。
总结来说,installModule
的作用就是通过递归将各个子模块上的state
,getters
,mutations
和actions
整合到Store
类的state
,getters
,mutations
和actions
四个属性上,从而可以在组件中通过this.$store.xx
来操作。
installModule
函数的实现过程
installModule
函数主要是安装state
,getters
,mutations
和actions
,如果有子模块的话,重复之前的安装步骤,看下代码的实现:
//将格式化后的数据递归绑定到getters/mutations/actions
const installModule = (store, rootState, path, modules) => {
//分别安装state,getters,mutations和actions
installState(path, rootState, modules);
installGetters(store, modules);
installMutations(store, modules);
installActions(store, modules);
//如果有子模块的话
if (Object.keys(modules._children).length > 0) {
Object.keys(modules._children).forEach(childModuleName => {
installModule(
store,
rootState,
path.concat(childModuleName),
modules._children[childModuleName]
);
});
}
};
installModule
函数的四个参数表示什么意思?
-
store
:表示当前Store
类。 -
rootState
:表示当前要安装模块的state
模块。 -
path
:表示当前模块和所有父级模块的模块名的集合。在递归过程中,如果有子模块的话会将当前模块的模块名合并子模块的模块名作为下一个installModule
函数中的path
参数。举个例子:如果根模块有子模块city
,city
子模块又有district
子模块,因为有三级,按层级划分就是根模块,city
模块和district
模块,那这个path
在注册每个模块时处理结果如下:
第一次对根模块执行installModule
,path
为[]
。
第二次对city
模块执行installModule
,path
为[ 'city' ]
。
第三次对district
模块执行installModule
,path
为[ 'city' , 'district' ]
。 -
modules
:表示当前要安装的模块。
installState
实现过程
const installState = (path, rootState, modules) => {
if (path.length > 0) {
let parentState = path.slice(0, -1).reduce((root, current) => {
return root[current];
}, rootState);
Vue.set(parentState, path[path.length - 1], modules.state);
}
};
如果不是根模块,那就获取它父级模块的state
,并在父级模块的state
上添加一个新属性,属性名为当前的模块名,属性值为当前模块的state
,最终store
上的state
参考如下结构:
{
state_country: "中国",
city: {
state_city: "上海",
district: {
state_district: "黄浦区"
}
}
};
vuex
中的state
是响应式的,state
改变会触发视图更新。实现方式就是在父级模块的state
上用Vue.set
添加子级模块的state
,这样子模块的state
变成响应式数据。
installGetters
实现过程
const installGetters = (store, modules) => {
const getters = modules._rawModule.getters;
if (getters) {
Object.keys(getters).forEach(getterName => {
Object.defineProperty(store.get
ters, getterName, {
get: () => {
return getters[getterName](store.state);
}
});
});
}
};
installGetters
就是将modules
的getters
绑定到store
上的getters
属性上。通过Object.defineProperty
将modules
的getters
属性名遍历绑定到store
的getters
上,在组件中获取store.getters
上的属性实际上获取的是modules
上的属性,相当于通过Object.defineProperty
做了数据劫持。
installMutations
实现过程
const installMutations = (store, modules) => {
const mutations = modules._rawModule.mutations;
if (mutations) {
Object.keys(mutations).forEach(mutationName => {
let temp =
store.mutations[mutationName] || (store.mutations[mutationName] = []);
temp.push((name, payload) => {
mutations[name](modules.state, payload);
});
});
}
};
installMutations
就是将modules
的mutations
绑定到store
上的mutations
对象上。store
的mutations
是对象,键名是mutations
的函数名,键值是数组容器,存放所有同名的mutation
函数,mutation
是不怕重名的,重名的话将逐个执行。mutation
函数需要两个参数,一个是mutations
的state
,另一个是用户传入的参数,最终store
上的mutations
参考如下结构:
{
mutation_city: [
(state, val) => (state.state_city = "上海"),
(state, val) => (state.state_city = val)
],
mutation_country: [
(state, val) => (state.state_country = val)
]
};
在组件中调用mutation
的话是用this.$store.commit( "mutationName" , value )
触发,那mutation
和commit
是怎么关联的呢?
commit = (mutationName, payload) => {
this.mutations[mutationName].forEach(fn => fn(mutationName, payload));
}
commit
是store
类上的方法,它第一个参数为要触发的mutation
名称,第二个为传入的值。它获取mutation
上名字为mutationName
的方法集合,然后传入payload
依次执行。
installActions
实现过程
installActions
实现过程和installMutations
实现过程差不多,就是触发mutation
的为commit
,而出发action
的为dispatch
。