二、模块初始化
在Store的constructor
中,会执行一系列数据的初始化,其中会执行this._modules = new ModuleCollection(options)
,假设写入的option是这样的结构
const modulesA = {
namespaced: true,
state: {
conut: 1
},
getters: {
computedConut(state) {
return state.conut + 1
}
},
mutations: {
add(state) {
state.conut + 1
}
},
actions: {
addCount(context) {
context.commit('add')
}
}
}
const store = new Vuex.Store({
modules: {
modulesA
},
state: {
conut: 1
},
getters: {
computedConut(state) {
return state.conut + 1
}
},
mutations: {
add(state) {
state.conut + 1
}
},
actions: {
addCount(context) {
context.commit('add')
}
}
})
在执行ModuleCollection
的constructor的时候,参数rawRootModule
就是之前的options,constructor会执行this.register
,第一参数传入空数组,第二个参数传入rawRootModule
,第三个参数传false。在这里作者给出的注释是// register root module (Vuex.Store options)
注册根部module。register
函数首先会通过const newModule = new Module(rawModule, runtime)
,rawModule为rawRootModule
,runtime为false。Module
在执行constructor的时候会执行this._rawModule = rawModule
来保存传入的rawModule
, 执行const rawState = rawModule.state
来保存rawModule为
的state,也就是传入的对象根部的state,接着通过this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
来保存state,也就是说根部的state既可以是对象的形式,也可以是一个函数执行后返回对象的形式。这样register
函数的newModule
就是一个Module
实例。接着register
函数会判断path的长度,path在首次执行传入的是一个空数组,也就是会执行this.root = newModule
把我们实例化的newModule
作为root。接着会判断rawModule.modules
在上边的demo中,是有编写modules的,所以会通过Object.keys
遍历rawModule.modules
,去执行 this.register(path.concat(key), rawChildModule, runtime)
,path在第一次是一个空数组,但执行到了这一步,会通过concat,把遍历到的modules的key添加到数组中,这样path就成了['modulesA']
,然后第二个参数为modulesA
的值。这样递归的执行register
函数进行注册,和第一次执行register
不同的是,会先去执行const parent = this.get(path.slice(0, -1))
,也就是执行get方法,由于对path进行了剔除最后一位的操作,所以传入的是一个空数组。get函数会对传入的path做一个reduce,reduce每次执行会把当前的key作为参数传入Module
实例的getChild
方法,执行并返回结果,Module实例的getChild方法会返回当前this._children[key]
。对于本次执行get
由于传入的path参数是一个空数组,所以会直接返回this.root,那么parent就是第一次执行register
的Module
实例。接着本次的register
会执行parent.addChild(path[path.length - 1], newModule)
,第一个参数传入path的最后一位,也就是moduleA
字符串,第二个参数传入的是本次register的Module
实例,addChild
会执行this._children[key] = module
这样就把ModuleA创建的Module实例存入了根部Module实例的_children
中,那么假如modulesA的modules执行到get方法的时候,就可以拿到modulesA的module实例作为parent,继续执行appendchild操作,这样就递归建立了Module实例之间的父子关系。
// src/module/module-collection.js
class ModuleCollection {
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
...
register (path, rawModule, runtime = true) {
...
const newModule = new Module(rawModule, runtime)
if (path.length === 0) {
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
// register nested modules
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
...
}
// src/module/module.js
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
// Store some children item
this._children = Object.create(null)
// Store the origin module object which passed by programmer
this._rawModule = rawModule
const rawState = rawModule.state
// Store the origin module's state
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
...
getChild (key) {
return this._children[key]
}
...
}
三、模块安装
执行new ModuleCollection(options)
会为store实例的_modules
挂载,之后会执行installModule
函数,作者给出的注释是// init root module.
,// this also recursively registers all sub-modules
以及// and collects all module getters inside this._wrappedGetters
,也就是说注册根部模块并且递归的安装所有子模块,并且会把所有模块的getter收集到this._wrappedGetters
中。
1、执行makeLocalContext拿到local
执行installModule
的时候第一个参数传入当前的store实例,第二个参数传入根部的state,第三个参数传入空数组,第四个参数传入根部的module实例。installModule
函数首先执行const isRoot = !path.length
,根据传入的path.length(首次是空数组)判断他是不是root,然后执行 const namespace = store._modules.getNamespace(path)
,getNamespace
函数会根据传入的path数组,获取他们的module,如果namespaced
为true,那么会返回key+/
,假如传入的path为['modulesA','modulesB']
,并且两者namespaced
都为true,则会返回modulesA/modulesB/
,首次执行传入的path为空数组,所以首次namespace
为一个空的字符串。接着判断如果第四个参数modulenamespaced
为true,那么会执行store._modulesNamespaceMap[namespace]
,也就是说store._modulesNamespaceMap
对象中存储的是namespaced为true的module实例,并且以namespace
作为key。接着会执行const local = module.context = makeLocalContext(store, namespace, path)
,makeLocalContext
函数整体看下来定义了一个local
对象,其中有dispatch
和commit
,接通过Object.defineProperties
为local定义了访问getters和state的返回结果,最终返回local。dispatch和commit都会判断Namespace
,如果不是一个空的字符串,也就是说module的namespaced为false,会直接注册,如果有Namespace
那么会执行type = namespace + type
,也就是说在vuex中注册的带有namesped的module中的mutations
和actions
最终访问的方式会是modulesA/modulesB/methods
,也就是说makeLocalContext函数为我们重写了commit和dispatch。之后定义的getter的访问方式同样也是通过Namespace
来判断,如果有,则会执行makeLocalGetters
,makeLocalGetters
会判断store._makeLocalGettersCache[namespace]
,也就是是否有了当前module的getter缓存,如果没有则会遍历getters,首先拿到key最后的getter名称,然后通过 Object.defineProperty
把当前的getter和全局的getter进行绑定,这样当访问全局的比如modulesA/modulesB/xxxx
就会访问到当前module的getter。state的访问方式和之前的不同,他是通过state.ModulesA.ModulesB.xxx
进行访问,对应getNestedState
函数,去执行return path.reduce((state, key) => state[key], state)
,这样当访问子模块的state,会一层层去找,最终返回访问module的state。这样在配置store的时候,我们无需去关注他的嵌套层级。
// src/store.js
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (module.namespaced) {
...
store._modulesNamespaceMap[namespace] = module
}
...
const local = module.context = makeLocalContext(store, namespace, path)
...
}
/**
* make localized dispatch, commit, getters and state
* if there is no namespace, just use root ones
*/
function makeLocalContext (store, namespace, path) {
const noNamespace = namespace === ''
const local = {
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
...
if (!options || !options.root) {
type = namespace + type
...
}
return store.dispatch(type, payload)
},
commit: noNamespace ? store.commit : (_type, _payload, _options) => {
...
if (!options || !options.root) {
type = namespace + type
...
}
store.commit(type, payload, options)
}
}
// getters and state object must be gotten lazily
// because they will be changed by vm update
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path)
}
})
return local
}
function makeLocalGetters (store, namespace) {
if (!store._makeLocalGettersCache[namespace]) {
const gettersProxy = {}
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
// skip if the target getter is not match this namespace
if (type.slice(0, splitPos) !== namespace) return
// extract local getter type
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.
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
store._makeLocalGettersCache[namespace] = gettersProxy
}
return store._makeLocalGettersCache[namespace]
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!