本文参考珠峰架构公开课之
vuex
实现原理
在使用vuex
过程中,如果数据量很大可以用vuex
的modules
实现模块化。modules
实现过程个人认为可以分为数据注册和模块的安装,本篇主要将的是数据注册。
什么是数据注册?
数据注册是将用户传入的options
做了一次数据的格式化,转换成模块树状结构,实现父子模块的整合。比如用户传入的options
如下:
export default new Vuex.Store({
state: { state_country: "中国" },
getters: {},
mutations: {},
actions: {},
modules: {
city: {
state: { state_city: "上海" },
getters: {},
mutations: {},
actions: {}
}
}
});
转换好的数据结构如下:
{
root: {
_rawModule: {
state: { state_country: "中国" },
getters: {},
mutations: {},
actions: {},
modules: {
city: {
state: { state_city: "上海" },
getters: {},
mutations: {},
actions: {}
}
}
},
_children: {
city: {
_rawModule: {
state: { state_city: "上海" },
getters: {},
mutations: {},
actions: {}
},
_children: {},
state: { state_city: "上海" }
}
},
state: { state_country: "中国" }
}
}
可以发现,options
传入的数据被处理成了一个新的数据结构。源码中每个模块的生成通过一个module
类来转换。
Module
module
的代码如下:
class Module {
constructor(rawModule) {
this._children = Object.create(null);
this._rawModule = rawModule;
this.state = rawModule.state;
}
}
在
vue
全家桶的源码中,如果命名的变量名是raw
开头的话代表是未经过加工的数据。
传入options
单个模块的未经过加工的数据,返回一个包含_rawModule
,_children
,state
三个属性的数据结构:
-
_children
:用于存放子模块。 -
_rawModule
:存储自己模块的伪(未被加工)模块时的内容。 -
state
:存储自己模块的数据内容。
Module
类是对单个模块的处理,而vuex
中支持多模块,如果当前模块有子模块的话需要放到_children
中,这时候需要通过ModuleCollection
类来处理。
ModuleCollection
先来看下源码:
class ModuleCollection {
constructor(options) {
this.register([], options);
}
register(path, rawModule) {
let newModule = new Module(rawModule);
if (!this.root) {
this.root = newModule;
} else {
let parentModule = path.slice(0, -1).reduce((root, moduleName) => {
return root._children[moduleName];
}, this.root);
parentModule._children[path[path.length - 1]] = newModule;
}
//如果子元素还有modules,需要递归注册
if (rawModule.modules) {
Object.keys(rawModule.modules).forEach(res => {
this.register(path.concat(res), rawModule.modules[res]);
});
}
}
}
ModuleCollection
核心的功能是register
函数,将所有的子模块整合成树状结构。
register
做了什么?
- 将传入的
options
通过Module
类生成新的模块。 - 将生成的新模块添加到它的父元素上。
- 如果当前的模块有子元素,重新执行
register
,也就是重复第1,2步骤。
register
函数的path
和rawModule
两个参数的作用?
-
rawModule
:表示当前未加工的模块。 -
path
:表示当前模块和所有父级模块的模块名的集合。在递归过程中,如果有子模块的话会将当前模块的模块名合并子模块的模块名做为下一个register
函数中的path
参数。举个例子:如果根模块有子模块city
,city
子模块又有district
子模块,因为有三级,按层级划分就是根模块,city
模块和district
模块,那这个path
在注册每个模块时处理结果如下:
第一次对根模块执行register
,path
为[]
。
第二次对city
模块执行register
,path
为[ 'city' ]
。
第三次对district
模块执行register
,path
为[ 'city' , 'district' ]
。
为何要判断是否是this.root
?
通过实例化ModuleCollection
返回的数据结构,它的根模块数据是包在root
对象里的。因为根模块调用register
,也就是第一次调用register
函数的时候是没有this.root
的,类里面没有这个属性,所以一定返回false
,于是就将当前模块赋值给this.root
,因为this.root
已被赋值,所以不会再进入这个判断。
除了以上问题,还有个比较难理解的是下面这段代码:
let parentModule = path.slice(0, -1).reduce((root, moduleName) => {
return root._children[moduleName];
}, this.root);
parentModule._children[path[path.length - 1]] = newModule;
path.slice(0, -1)
表示父级路径的集合,然后通过reduce
获取当前模块的上级模块,并将当前模块添加到上级模块的_children
里。
文章的最后
讲完了数据注册,接下来还有模块的安装,模块的安装最主要的功能就是将注册完的数据里每个模块的state
,getters
,mutation
,action
递归安装到Store
类上,从而用户可以在组件中可以通过this.$store.xxx
来操作数据。
网友评论