Vuex 源码解析2 (之 Module)
一. Module/module.js (module 类)
1. 首先附上源码
import { forEachValue } from '../util'
// Module 构造类
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
// 创建一些空的Store 子模块对象
this._children = Object.create(null)
// 保存module
this._rawModule = rawModule
// 保存module的state
const rawState = rawModule.state
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
// 获取 namespace
get namespaced () {
return !!this._rawModule.namespaced
}
// 插入一个子module, 存入_children中
addChild (key, module) {
this._children[key] = module
}
// 移除一个子module
removeChild (key) {
delete this._children[key]
}
// 根据key获取子module
getChild (key) {
return this._children[key]
}
// 更新module
update (rawModule) {
this._rawModule.namespaced = rawModule.namespaced
if (rawModule.actions) {
this._rawModule.actions = rawModule.actions
}
if (rawModule.mutations) {
this._rawModule.mutations = rawModule.mutations
}
if (rawModule.getters) {
this._rawModule.getters = rawModule.getters
}
}
// 遍历child
forEachChild (fn) {
forEachValue(this._children, fn)
}
// 遍历getter
forEachGetter (fn) {
if (this._rawModule.getters) {
forEachValue(this._rawModule.getters, fn)
}
}
// 遍历action
forEachAction (fn) {
if (this._rawModule.actions) {
forEachValue(this._rawModule.actions, fn)
}
}
// 遍历mutation
forEachMutation (fn) {
if (this._rawModule.mutations) {
forEachValue(this._rawModule.mutations, fn)
}
}
}
该类主要提供store module 的保存,命名空间获取及相关模块添加/删除/更新等操作api。 同时对各个模块内的对外state操作公共函数进行遍历/收集
二. Module/module-collection.js (module收集)
1. 先附上源码
import Module from './module'
import { assert, forEachValue } from '../util'
// module 收集类
export default class ModuleCollection {
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
// 获取指定路径下的module
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
/*
获取namespace, 当namespaced is true 时,将返回 ‘moduleName/name’
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的。
这使得多个模块能够对同一 mutation 或 action 作出响应(当然也会造成混淆)。
如果希望模块更加独立/自包含/提高可重用性,可以通过添加 namespaced: true 的方式使其成为命名
空间模块。
当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
*/
getNamespace (path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + '/' : '')
}, '')
}
update (rawRootModule) {
update([], this.root, rawRootModule)
}
// 注册模块
register (path, rawModule, runtime = true) {
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, rawModule)
}
// 新建一个Module对象
const newModule = new Module(rawModule, runtime)
// path 为空代表根节点
if (path.length === 0) {
this.root = newModule
} else {
// 获取父级module
const parent = this.get(path.slice(0, -1))
// 在父module中插入一个子module
parent.addChild(path[path.length - 1], newModule)
}
// 递归注册module
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
// 注销
unregister (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
if (!parent.getChild(key).runtime) return
parent.removeChild(key)
}
}
// 更新
function update (path, targetModule, newModule) {
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, newModule)
}
// 执行更新module操作
targetModule.update(newModule)
// 更新嵌套的module
if (newModule.modules) {
for (const key in newModule.modules) {
if (!targetModule.getChild(key)) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
`[vuex] trying to add a new module '${key}' on hot reloading, ` +
'manual reload is needed'
)
}
return
}
update(
path.concat(key),
targetModule.getChild(key),
newModule.modules[key]
)
}
}
}
const functionAssert = {
assert: value => typeof value === 'function',
expected: 'function'
}
const objectAssert = {
assert: value => typeof value === 'function' ||
(typeof value === 'object' && typeof value.handler === 'function'),
expected: 'function or object with "handler" function'
}
const assertTypes = {
getters: functionAssert,
mutations: functionAssert,
actions: objectAssert
}
function assertRawModule (path, rawModule) {
Object.keys(assertTypes).forEach(key => {
if (!rawModule[key]) return
const assertOptions = assertTypes[key]
forEachValue(rawModule[key], (value, type) => {
assert(
assertOptions.assert(value),
makeAssertionMessage(path, key, type, value, assertOptions.expected)
)
})
})
}
function makeAssertionMessage (path, key, type, value, expected) {
let buf = `${key} should be ${expected} but "${key}.${type}"`
if (path.length > 0) {
buf += ` in module "${path.join('.')}"`
}
buf += ` is ${JSON.stringify(value)}.`
return buf
}
该类做的事情主要是注册 / 收集module, 获取命名空间,相关module的递归更新模块等。
下一节将继续介绍Vuex plugins 相关源码