Appearance
namespace
javascript
function installModule (store, rootState, path, module, hot) {
// 不开启命名空间, getters 不能重名,type 相同的 action 和 mutation 会被合并在
// 一个数组中,dispatch 时相同 tpye 的 action 都会执行
// commit 时 相同 type 的 mutation 都会执行
// 假设 b 模块的父模块是 a 模块,a 模块和 b 模块都开启命名空间
// a 模块下 action、mutation 的 type 前都会加上 a/,getter 的 key 前会加上 a/
// b 模块下 action、mutation 的 type 前都会加上 a/b/,getter 的 key 前会加上 a/b/
// 如果 a 模块还有子模块 c、子模块 d,如果 c、d 模块不开启命名空间
// 那么c、d 模块 type 前只会加上 a/,getter 的 key 前只会加上 a/
// a、c、d 模块 type 相同的 action 和 mutation 会被合并在一个数组中
// a、c、d 模块的 getter 不能重名
// 因此安装模块时第一步是获取 namespace,如果有的话 action、mutation、getter 的
// type 都需要加上 namespace,dispatch 或 commit 时如果有 namespace 就也在 tppe
// 前加上 namespace,这样就能实现隔离。因此 local.dispatch 和 local.commit 属性
// 的值需要判断 namespace 是否为空字符串,如果是空字符串,没有开启命名空间,值就是
// store.dispath,否则开启了命名空间,值是包装函数,内部让
// type = namespace + type,最后执行 store.dispath(type, payload),就能找到
// namespace 下的数组,
// local.getters 也需要判断 namespace 是否为空字符串,如果是空字符串,没有开启命名
// 空间,值就是 store.getters,否则开启命名空间,值是一个对象,对象里的每个 key
// 都是以 namespace 开头的 getter 的 key,为了避免每次都从所有 getters 中查找以
// namespace 开头的 getter 然后再放进对象中,通过
// store._makeLocalGettersCache 对象缓存查找结果。对象的 key 是 namespace
// value 是一个对象 getterProxy,保存着所有以该 namespace 开头的 getter。
// getterProxy 的 key 就是我们定义的 getter 的 key,最前面不会有 namespace,
// 这样我们在查看 local.getters 时就不会有 namespace 干扰了,value 就是
// store.getters[type] 对应的 wrappedGetter。gettersProxy 实现了劫持,当访问
// 我们本地定义的 key 时才会获取对应的 wrappedGetter。保证总是获取到最新的 getter。
// 因此获取 namespace 下的 getter 时,如果
// store._makeLocalGettersCache[namespace] 存在,直接返回 getterProxy。
// 如果不存在,先获取 namespace 的长度,Object.keys(store.getters) 获取所有
// getter 的名字,遍历每一项并 slice(0, namespace.length) 开始截取名字前缀,
// 如果截取出来前缀和 namespace 不相等说明这个 getter 不是 namespace 下的 getter,
// 否则是这个namespace 下的 getter,需要保存进 getterProxy 中,
// 通过 Object.defineProperty 为 getterProxy 添加,key 是我们本地写的 key,不带
// namespace,而 registerGetter 时加上了 namespace,因此
// slice(namespace.length) 获取我们本地定义时的 getter 的 key,
// value 就是 store.getters[type]。遍历完后就找出了所有该 namespace 下的所有
// getter 了,最后让store._makeLocalGettersCache[namespace] = gettersProxy。
// state 不受到 namespace 影响,因此无需处理。
// 如路径 ['a', 'b'] 中 a 模块开启命名空间,b 模块不开启命名空间那么这个路径的命名
// 空间就是 /a,如果 b 模块开启命名空间那么这个路径的命名空间就是 /a/b。
// Module 类中定义一个 namespaced 属性标识是否开启命名空间,
// 访问它时返回 this._rawModole.namespaced。
// ModuleCollection 类中定义一个 getNameSpace 方法,传入 path 拼接处该路径的
// namespace,内部从根模块开始不断向下找,直到找到路径中最后一个模块,reduce 初始值
// 是 '',过程中发现当前模块开启了命名空间的话结果就会拼接上 'moduleName/',
// 否则拼接 '',最后将这个结果返回就是 namespace 的值,
const namespace = store._modules.getNamespace(path)
// local(module.context) 是一个对象,保存着当前模块的
// dispatch、commit、getters、state
const local = module.context = makeLocalContext(store, namespace, path)
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, type, handler, local)
})
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}
function makeLocalContext (store, namespace, path) {
const noNamespace = namespace === ''
const local = {
dispatch: noNamespace ? store.dispatch : (type, payload) => {
type = namespace + type
if (__DEV__ && !store._actions[type]) {
return
}
return store.dispatch(type, payload)
},
commit: noNamespace ? store.commit : (_type, _payload, _options) => {
type = namespace + type
if (__DEV__ && !store._mutations[type]) {
return
}
store.commit(type, payload, options)
}
}
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path)
}
})
return local
}
// 创建获取 local.getters 时的缓存表
function makeLocalGetters (store, namespace) {
if (!store._makeLocalGettersCache[namespace]) {
const gettersProxy = {}
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
// 这个 getter 的 key 不是以 namespace 开头
// skip if the target getter is not match this namespace
if (type.slice(0, splitPos) !== namespace) return
// 本地定义的 getter 的 key
// extract local getter type
const localType = type.slice(splitPos)
// 保存这个结果,劫持保证获取的总是最新的 getter
// Add a port to the getters proxy.
// Define as getter property because
// we do not want to evaluate the getters in this time.
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
store._makeLocalGettersCache[namespace] = gettersProxy
}
return store._makeLocalGettersCache[namespace]
}
class Module {
get namespaced () {
return !!this._rawModule.namespaced
}
}
class ModuleCollection {
getNamespace (path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + '/' : '')
}, '')
}
}