Vuex 源码阅读系列会分为三篇,前两篇会主要解析 Vuex 初始化中做了什么,最后一篇主要解析各种常用 API。
因为涉及代码较多,更推荐在 PC 浏览。
installModule 解析
接下来看 installModule
的实现
// installModule(this, state, [], this._modules.root)
function installModule (store, rootState, path, module, hot) {
// 判断是否为 rootModule
const isRoot = !path.length
// 获取 namespace,root 没有 namespace
// 对于 modules: {a: moduleA} 来说
// namespace = 'a/'
const namespace = store._modules.getNamespace(path)
// 为 namespace 缓存 module
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module
}
// 设置 state
if (!isRoot && !hot) {
// 以下逻辑就是给 store.state 添加属性
// 根据模块添加
// state: { xxx: 1, a: {...}, b: {...} }
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
// 该方法其实是在重写 dispatch 和 commit 函数
// 你是否有疑问模块中的 dispatch 和 commit
// 是如何找到对应模块中的函数的
// 假如模块 A 中有一个名为 add 的 mutation
// 通过 makeLocalContext 函数,会将 add 变成
// a/add,这样就可以找到模块 A 中对应函数了
const local = module.context = makeLocalContext(store, namespace, path)
// 以下几个函数遍历,都是在
// 注册模块中的 mutation、action 和 getter
// 假如模块 A 中有名为 add 的 mutation 函数
// 在注册过程中会变成 a/add
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)
})
// 这里会生成一个 _wrappedGetters 属性
// 用于缓存 getter,便于下次使用
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)
})
}
resetStoreVM 解析
接下来看 resetStoreVM
的实现,该属性实现了状态的响应式,并且将 _wrappedGetters
作为 computed
属性。
// resetStoreVM(this, state)
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// 设置 getters 属性
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
// 遍历 _wrappedGetters 属性
forEachValue(wrappedGetters, (fn, key) => {
// 给 computed 对象添加属性
computed[key] = () => fn(store)
// 重写 get 方法
// store.getters.xx 其实是访问了
// store._vm[xx]
// 也就是 computed 中的属性
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// 使用 Vue 来保存 state 树
// 同时也让 state 变成响应式
const silent = Vue.config.silent
Vue.config.silent = true
// 当访问 store.state 时
// 其实是访问了 store._vm._data.$$state
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// 确保只能通过 commit 的方式改变状态
if (store.strict) {
enableStrictMode(store)
}
}
最后
以上是第二部分的 Vuex 源码解析,介绍了 Vuex 如何在初始化的过程创建 module 树,并且如何将 state 变为响应式。