2、注册Mutation,Action,Getter和子模块
- 注册Mutation
拿到local之后,会执行
module.forEachMutation也就是根部Module实例的forEachMutation函数,forEachMutation函数会判断当前的结构中是否有mutations,如果有的话,那么会执行 forEachValue(this._rawModule.mutations, fn),forEachValue函数是通过Object.keys遍历传入的第一个参数obj,然后把obj每一项的value作为fn的第一个参数,每一项的key作为fn的第二个参数,调用fn函数。也就是说module.forEachMutation函数传入的回调函数,最终被执行,并且这个回调函数中的第一个参数mutation就是传入的module实例的mutation中的具体的某项值,第二个参数key就是对应的key。回调函数首先通过namespace + key定义了namespacedType首次执行namespace为空,则namespacedType为具体的mutation的key,然后执行了registerMutation(store, namespacedType, mutation, local)。registerMutation函数在第一次调用会初始化store._mutations[type]为空数组,然后往这个数组中push了wrappedMutationHandler函数,如果执行了store._mutations[type]函数,那么会执行handler.call(store, local.state, payload),也就是以store实例为this,调用该mutation,并把local.state作为第一个参数,payload作为第二个参数(调用动态写入)。local.state在makeLocalContext函数中经过处理,假如这里访问的local.state是b模块的state(假设有a模块,a模块嵌套b模块),最终会被代理到state.a.b.xxx,正确的访问到当前module对应的state。执行module.forEachMutation之后,Mutation会注册完毕
function installModule (store, rootState, path, module, hot) {
...
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
...
}
...
function registerMutation (store, type, handler, local) {
const entry = store._mutations[type] || (store._mutations[type] = [])
entry.push(function wrappedMutationHandler (payload) {
handler.call(store, local.state, payload)
})
}
export default class Module {
...
forEachMutation (fn) {
if (this._rawModule.mutations) {
forEachValue(this._rawModule.mutations, fn)
}
}
}
export function forEachValue (obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key))
}
- 注册Action
之后会执行
module.forEachAction函数,对action进行注册, module.forEachAction和module.forEachMutation的逻辑是比较相像的,也是首先判断当前的store结构有没有actions,如果有的话那么遍历的执行module.forEachAction传入的回调函数,把具体的action值作为第一个参数,key作为第二个参数。执行回调函数,首先拿到type(判断action值是否有root,如果有则不拼接namespace),然后拿到handler具体的处理函数(如果传入的是对象,则获取其中的handler定义的函数)。接着执行registerAction函数,和registerMutation函数相似,他会定义store._actions[type]为空数组,然后pushwrappedActionHandler函数,当执行这个函数时,会给handler传入{ dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }这也是文档中提到的action的第一个参数可以拿到根部的getter和根部的state。然后判判断如果执行action的返回值有then方法(promise),那么调用Promise.resolve(res)。
function installModule (store, rootState, path, module, hot) {
...
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
...
}
...
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload)
if (!isPromise(res)) {
res = Promise.resolve(res)
}
if (store._devtoolHook) {
...
} else {
return res
}
})
}
- 注册Getter
注册完action之后会执行
module.forEachGetter函数,他也是通过namespace和当前的key进行拼接,接着调用registerGetter函数,之前的store._mutations[type]和store._actions[type]对应的都是一个数组,而这里的store._wrappedGetters[type]对应的是一个函数wrappedGetter,如果执行wrappedGetter那么参数为 local.state,local.getters,store.state,store.getters(对应文档中)。
function installModule (store, rootState, path, module, hot) {
...
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
...
}
function registerGetter (store, type, rawGetter, local) {
if (store._wrappedGetters[type]) {
...
return
}
store._wrappedGetters[type] = function wrappedGetter (store) {
return rawGetter(
local.state,
local.getters,
store.state,
store.getters
)
}
}
- 注册子模块
最后会执行
module.forEachChild,首先会判断当前的module是否有_children,如果有子模块的话,那么执行installModule(store, rootState, path.concat(key), child, hot),执行传入的path参数会对key和当前的path进行拼接。也就是说当执行子模块的installModule,isRoot为false。和root模块不同的是,他会首先会通过
getNestedState(this.state, path.slice(0, -1))拿到parentState,然后通过path[path.length - 1]拿到最后一个模块的名称,接着调用store._withCommit函数,传入一个回调函数,这个回调函数中会执行Vue.set(parentState, moduleName, module.state),也就是说通过Vue.set,在parentState上以当前的模块名作为key,当前的state作为value进行添加。这也是为什么全局在访问子模块的state的时候可以通过state.a.b.xxx进行访问。这样如果发现有子的module会递归的执行installModule,这样就会递归进行了每一个模块的注册。
function installModule (store, rootState, path, module, hot) {
...
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
if (__DEV__) {
...
}
Vue.set(parentState, moduleName, module.state)
})
}
...
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
...
}