共计 3773 个字符,预计需要花费 10 分钟才能阅读完成。
module 与 moduleCollection 你一定要会啊!Vuex 源码学习(五)加工后的 module
在组件中使用 vuex 的 dispatch 和 commit 的时候,我们只要把 action、mutation 注册好,通过 dispatch、commit 调用一下方法名就可以做到。
使用方式
vue 组件内
//in vue component
this.$store.commit(‘setName’,{name : ‘xLemon’});
this.$store.commit(‘list/setName’,{name : ‘xLemon’});
vuex 的 mutation 中
// in mutation.js
export const setName = function(state,payload){
state.name = payload.name;
}
// in list mutation.js 在名为 list 的模块下的 mutation(有自己的命名空间)
export const setName = function(state,payload){
state.name = payload.name;
}
我们传递的只是一个字符串,commit 是如何找到注册 mutation 时的同名方法的呢?有命名空间的这种 mutation 是如何被找到并且执行的呢?
上源码
属性的意义
_actions 用于存放所有注册的 action
_mutations 用于存放所有注册的 mutation
被注册的 action 和 mutation 如何被放到对应的属性中的呢?
轮到 installModule 函数要出马了。
installModule 的意义是初始化根模块然后递归的初始化所有模块,并且收集模块树的所有 getters、actions、mutations、以及 state。看一下 installModule 的代码,installModule 并不是在类的原型上,并不暴露出来,属于一个私有的方法,接收五个参数。
store(Vuex.store 的实例对象。
rootState (根结点的 state 数据)。
path 被初始化模块的 path(前两张讲过 path 的意义)。
module 被初始化的模块。
hot 热更新(并不关键)
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
// 获取全名
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (module.namespaced) {
// 设置命名空间对照 map
store._modulesNamespaceMap[namespace] = module
//console.log(store._modulesNamespaceMap);
}
// set state
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length – 1]
// 把子模块的 state(数据) 绑定到父模块上(按照层级)
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
const local = module.context = makeLocalContext(store, namespace, path)
// 使用模块暴露出来的方法来注册 mutation、action、getter
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
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)
})
}
这个函数虽然只有 40 多行,但处于一个承上启下的关键点,这一章只会分析如何收集 mutation 与 action 其余的内容会再下一章讲述。
installModule 首先获取一下这个模块的命名(我称之为全名)依赖_modules(ModuleCollection 实例对象)的 getNamespace 方法。
根据模块的 path,path 有从根结点到当前节点这条路径上按顺序排序的所有模块名 (根结点没有模块名,并没有设置在 path,所以根模块注册时是个空数组,他的子模块的 path 就是 [ 子模块的名字])。那么 Vuex 如何整理模块名的呢?
效果:
如果这个模块有自己的命名空间(namespaced 为 true)这个模块的全名就是父模块的全名 + 自己的模块名,
如果这个模块没有自己的命名空间(namespaced 为 false)这个模块的全名就是父模块的全名
为什么会是这样?分析一下代码
getNamespace (path) {
let module = this.root // 根模块
return path.reduce((namespace, key) => {
// 根模块的 path 是个空数组不执行
// path 的第一项是根模块的儿子模块。
// 获取儿子模块 并且将替换 module(path 的下一项就是儿子模块中的子模块了)
// 下次累加 就是这个 module(轮到子模块了)去找它(子模块)的子模块
module = module.getChild(key)
// 查看儿子模块是不是设置了命名空间
// 如果设置了这个模块的全名就增加自己的模块名和一个 ’/’ 分割后面的模块名,
// 没有的话返回一个 ”,
// reduce 累加可以把这个名称进行累加
return namespace + (module.namespaced ? key + ‘/’ : ”)
}, ”)
}
获取完模块的全名了,之后我们看一下这两个函数
module.forEachAction
module.forEachMutation
在上一章节 module 提供了遍历自己内部的 action、mutation 的方法。
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
const namespacedType = namespace + key
这句话 就是拼接出真正的 mutation、action 的名字
模块全名 +mutation/action 的名字。也就是一开始我举例的 list/setName 是这个 mutation 的全名(被调用的时候用)
this.$store.commit(‘list/setName’,{name : ‘xLemon’});
名称已经获取到了,下一步怎么办?
把这些函数按照对应名字放到之前说的_actions、_mutations 属性中啊
看一下这个名字的 mutation 有没有被注册过,没有就声明一下,然后 push 进去。
如果这个名字的 mutation 被注册过,就 push 进去。
action 同理
小彩蛋 设置两个不同模块的同名 mutation(全名一样哦)这两个 mutation 都会执行,action 也是一样的。
总结
action 和 mutation 在被 dispatch 和 commit 调用前,
首先遍历模块树获取每个模块的全名。
把模块内的 action 和 mutation 加上模块全名,整理成一个全新的名字放入_actions、_mutations 属性中。
dispacth 和 commit 如何调用 aciton 和 mutation 的将在下章讲述
下一章:我们讨论 action 和 mutation 如何被调用的(调用篇)。
我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~