Vuex源码分析(二)-- state
首先总结
state
是Store
实例的一个属性- 通过创建
Store
实例生成的state
,根节点上的state
始终包含全局state
以及相应的模块名,不管子模块的namespaced
是否为true
namespaced
字段只影响[getters, mutations, actions]
的命名前缀
const store = new Vuex.Store({
state: {a : 1}, // 这个state可以是一个对象,也可以是一个方法
modules: { // 存放子模块的地方,module2为子模块的命名空间
module2: {
state: {b : 2}
}
}
})
vm.$store.state = { a : 1, module2 : { b : 2 } }
这里的state
已经不像原始options
那样很多层级了,那么Store
的内部结构如何?又是如何简化 state
结构的呢?由于源码中存在很多非state
相关处理,于是做了相应的简化处理
首先是构造器
export class Store {
constructor (options = {}) {
// new Store()时,如果Vue未注册Vuex,则直接注册
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
...
// 把state赋值给了 vm.data.$$state,getters赋值给了vm.computed
// 然后使用vue的 $watch 方法,进行监控
this._watcherVM = new Vue()
// 这里直接处理options中的属性,将其生成一个Module树
// 每个 Module 对象都包含[state, getters, mutations, actions, modules]属性
// modules 是子模块,也是 Module 对象
this._modules = new ModuleCollection(options)
...
// 处理完后 state = { a : 1 },此时还没有子模块的state
const state = this._modules.root.state
...
// 汇总处理各模块对象上的[state, getters, mutations, actions]
// 于是根节点上的state就包含了所有state
installModule(this, state, [], this._modules.root)
// destroy原来的this._watcherVM对象,然后新建一个new Vue() 对属性重新监控
resetStoreVM(this, state)
}
get state () {
return this._vm._data.$$state
}
// 提示不允许直接设置 state
set state (v) {
if (__DEV__) {
assert(false, `use store.replaceState() to explicit replace store state.`)
}
}
}
然后是模块收集器ModuleCollection
根据从Store
对象传来的options
属性,生成一系列Module
树
export default class ModuleCollection {
// rawRootModule ⇒ 原始options
constructor (rawRootModule) {
this.register([], rawRootModule, false)
}
/**
* 根据 options 对象,生成模块
*
* @path: 前置路径,是一个数组,目的是处理多 modules 的命名空间
* @rawModule: 当前module的原始options对象
*/
register (path, rawModule, runtime = true) {
const newModule = new Module(rawModule, runtime)
// 没有前置路径,表示根模块
if (path.length === 0) {
this.root = newModule
} else {
// 例:path = ['module1', 'module2', 'module3']
// parent = this.root._children['module1']._children['module2']
const parent = this.get(path.slice(0, -1))
// parent.addChild('module3', newModule) ⇒ parent._children['module3'] = newModule
parent.addChild(path[path.length - 1], newModule)
}
// 如果当前options.modules存在,依次建立子模块
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
}
每一个optons
对象,都会生成一个Module
对象,这个对象的_children
对象会储存modules
上的子模块,这个子模块也是一个Module
对象
export default class Module {
constructor (rawModule, runtime) {
// _children 存放的也是Module对象
this._children = Object.create(null)
// 保存原始options对象
this._rawModule = rawModule
// this.state = options.state
const rawState = rawModule.state
// 这里表明 state 既可以传一个对象,也可以传一个方法
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
}
这个方法是简化state
对象的核心方法
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
// 如果不是根节点,或者不是热部署,将当前节点的state赋值给父节点的state上
if (!isRoot && !hot) {
// getNestedState 表示获取根节点上指定路径上的state
// 例: path = ['module1', 'module2']
// getNestedState(rootState, path.slice(0, -1)) ⇒ rootState['module1']
// Vue.set(rootState['module1'], path[last], module.state)
// 这样根节点上的state就赋值上了 module2 的state, 即 rootState['module1']['module2'] = module.state
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
// 遍历子模块,依次注册
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}