乐趣区

Vuex源码学习(八)模块的context如何被创建以及它的作用

你不知道 action 与 mutation 怎么被调用的?赶紧回去看啊 Vuex 源码学习(七)action 和 mutation 如何被调用的(调用篇))
上两个小节已经讲述了 commit 与 dispatch 如何调用 mutation 与 action 的,但是 action 中有几个参数感觉涉及到了一些我们遗漏(故意不讲)的点。
模块的 context

在 installModule 的时候 给每个模块绑定了一个属性 context。通过 makeLocalContext 函数创建的,在注册 action、mutation 和 getters 都有使用。这个 context 是什么呢?
makeLocalContext 函数创建了一个什么东西
返回值 local 对象 由两个方法、两个属性构成的。
这个目的是什么?创建局部模块的 dispatch、commit、getters、state
也就是这个东西
我们按照类型分析
dispatch 与 commit
// 查看全名,如果没有全名 可能是根模块或者没有设置命名空间
const noNamespace = namespace === ”;
// 如果没有全名 就使用全局(store)上的 disptach
// 有全名的话 构建一个新的 dispatch
// 这个新的 dispatch 仍然接收三个参数(与 store 上的 dispatch 一样)
//
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
//unifyObjectStyle 对额外传入的_options 没有任何处理 只是确定一下位置
const args = unifyObjectStyle(_type, _payload, _options)
// options 值没有发生变化
const {payload, options} = args
let {type} = args
// 在 disptach 的时候是否指定选择 root(根)
// 如果 options 设置为 {root : true} 那么就会跳过下面
if (!options || !options.root) {
// 拼接真正的名字
type = namespace + type
if (process.env.NODE_ENV !== ‘production’ && !store._actions[type]) {
console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
return
}
}
// 调用(补全名字后)的 action
return store.dispatch(type, payload)
},
这段代码我们可以看出来,local 对象(也就是模块的 context 属性)中的 dispacth 会在未指定使用根模块名字时,会把 dispatch 调用的名字强行加上这个模块的全名,用这个 dispatch 调用的 action 都会变成你这个模块下的 action
所以 local 中的 dispatch 与 store 中的 disptach 有什么不同
通俗的讲我们想要调用 A 模块(有命名空间的某个 action B)需要做的是
this.$store.dispatch(‘A 模块的全名 / B 的名字 ’);
在 A 模块的 action 中想要使用 dispatch 来做一些事情。
actions.js
export const ajaxGetUserName = ({dispatch})=>{
// 这个时候用 dispatch 调用自己模块内的其余的 action 不需要加上全名
dispatch(‘ajaxGetUserAge’);
// 想要变成和根模块一样的 dispatch 需要加上一个 options 注明 {root : true}
// 这个时候 dispatch 就会变成全局的 不会主动帮你拼接全名了
}

export const ajaxGetUserAge = () => {
// do something
}
同理 local 对象下的 commit 也是做了同样的事情,
这里就不多加解释了,相信聪明的你早就可以举一反三了。
两个方法说完了,下面该讲两个属性了
getters 与 state

这两个属性就是我们的 getters 与 state,但是这是我们 local 对象中,也就是局部模块下的 getters 与 state。getters 与 state 如何创建的 getters 首先判断全名是不是为空,为空就返回 store 对象的 getters,有的话就创建局部 getters。与其说是创建不如说是代理
如何创建局部的 getters?代理的方式
makeLocalGetters 源码
function makeLocalGetters (store, namespace) {
// 设计思想
// 其实我们并不需要创建一套 getters,
// 只要我们在 local 中通过 getters 来获取一些局部模块的值的时候,
// 可以被代理到真正存放这些 getters 的地方。

// 创建代理对象
const gettersProxy = {}
// 找到切割点
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
// skip if the target getter is not match this namespace
// 得去 getters 里面找一下有没有这个 namespace 为前缀的 getter。
// 没有就找不到了
if (type.slice(0, splitPos) !== namespace) return

// extract local getter type
// 拿到模块内注册的那个局部的 getter 名字
// 全名是 set/getName
// localType 就是 getName
const localType = type.slice(splitPos)

// Add a port to the getters proxy.
// Define as getter property because
// we do not want to evaluate the getters in this time.
// 完成代理任务,
// 在查询局部名字是被代理到对应的 store.getters 中的(全名)getter
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
// 返回代理对象
return gettersProxy
}
创建局部的 getters 就是一个代理的过程,在使用模块内使用(没有加上命名空间的)getters 的名字,会被代理到,store 实例上那个真正的(全名的)getters。
state 这个相对来说就简单很多了
与代理类似,只是 state 只需要代理到 state 中对应那个模块的 state,这个就比较简单了。
创建完毕
context 是如何被创建的大家已经比较了解了。context 的作用是什么?(local 就是 contenxt)之前说过注册 mutation、action、getters 都用到了 local。用他们干什么?一一介绍
1. 注册 mutation
我们注册的 mutation 在被 commit 调用时,使用的 state 是局部的 state,当前模块内的 state,所以不用特殊方式 mutation 无法更新父(祖先)模块和兄弟模块的内容。
2. 注册 dispatch
dispatch 是可以调用到模块内的 mutation、disptach,也就是说它有更新模块内数据的能力,
但是只给了 dispatch 传入了 store 的 getters 与 state(虽然有了这俩你想要什么放在 vuex 的数据都能得到),并没有给 store 的 dispatch 与 mutation。
这就说名 dispatch 可以查看 store 中的所有数据,你放在 vuex 里面的数据我都可以看,但是你想改不使用特殊手段,不好意思只能改自己模块的。
3. 注册 gettersgetters 并没有改变数据的能力,你愿意怎么操作数据都可以,模块内的数据,全模块的数据都可以给你,你愿意怎么计算都可以。
在注册中我们可以看到,vuex 对这个改变数据的权限控制的很严格,但是查看数据控制的很松,改只能改自己模块的,查你愿意怎么看都 OK。
总结

context(也是 local)是经过一个 makeLocalContext 的函数创建的,里面有局部的 dispatch、commit 方法和 getters、state 属性。
局部的方法属性都是只能访问局部模块内的,除非在使用时额外传入 options({root:true}) 来解开局部模块的限制。
局部的 getters 是通过 makeLocalGetters 来实现的,主要思想是依靠代理的方式,把局部的名字的 getter 代理到 store 的 getters 中那个全名的 getter。
context 的作用可以帮助 dispatch 与 commit 控制更新数据的权限,帮助模块内 getters 拿到局部与全模块的数据。

这个章节结束,我们所有和模块有关的内容就已经完结了。对于模块做一个小的总结。
模块的意义

模块与模块链接把 Vuex 初始化传入的内容,整理成一个方便处理的模块树(方便)
模块让 action、mutation、getters、state 都有了自己的全名(设置 namespaced 为 true),起名字不再被约束,减少了命名冲突。
模块还给 action、mutation、getters 提供了局部上下文(context)让模块内的这些方法和属性,可以方便的修改模块内的数据以及获取全模块与模块内的数据。
dispatch 与 commit 也对模块进行了全力的支持(不支持不白做了吗),

所以模块为 Vuex 提供了很多方便, 方便的去获取数据、修改数据。那么 Vuex 真正的数据仓库在哪里?数据都存储在哪里?我们下一章见
我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

退出移动版