国家城乡住房建设厅网站网络营销战略的内容
大家好,今天给大家带来的是vuex(2.3.1)源码分析,希望能够能跟大家进行交流,欢迎提意见,写的不好的地方欢迎拍砖[github源码地址][1]首先我们先来看看vuex是如何跟vue项目一起结合使用的,以下是官方demo中的一个简单例子
(1)我们必须先创建一个store
import Vue from 'vue'
import Vuex from 'vuex'
import { state, mutations } from './mutations'
import plugins from './plugins'Vue.use(Vuex)export default new Vuex.Store({state,mutations,plugins
})
(2)将这个store传给vue的实例,这样我们就能够在vue中获取到这个store并且使用它的功能了
import 'babel-polyfill'
import Vue from 'vue'
import store from './store'
import App from './components/App.vue'new Vue({store, // inject store to all childrenel: '#app',render: h => h(App)
})
以上就是vuex的简单使用方法,然后接下来我们就开始来分析vuex的源码吧
-
目录结构
从目录结构可以看出,vuex是一个代码比较简洁的框架
-
index.js——入口文件
import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers'export default {Store,install,version: '__VERSION__',mapState,mapMutations,mapGetters,mapActions,createNamespacedHelpers
}
入口文件只做了一件事,就是导入了其他相关的文件,并且将vuex的功能export出去,相当于定义vuex对外使用的API
-
store.js——vuex的仓库,也是vuex中比较重要的一环
这个文件比较长,我们可以一点一点来分析:
总体来说,这个文件做了几件事,定义并导出了Store这个类和install方法,并执行了install这个方法,我们都知道,vue的所有插件都是通过install这个方法来安装的
import applyMixin from './mixin'
import devtoolPlugin from './plugins/devtool'
import ModuleCollection from './module/module-collection'
import { forEachValue, isObject, isPromise, assert } from './util'
一开始导入相关的方法,后面会解释这些方法的用处
let Vue // 定义了变量Vue,为的是引用外部的vue构造函数,这样vuex框架就可以不用导入vue这个库了
----------------------------------------------------------这是分割线----------------------------------------------------------------------------------------
接下来是定义Store这个类,从图中可以看出这个vuex中的外store对外提供的能力,包括常用的commit,dispatch,watch等
准备上传
先看看构造函数吧:
constructor (options = {}) {// 这个是在开发过程中的一些环节判断,vuex要求在创建vuex store实例之前必须先使用这个方法Vue.use(Vuex)来安装vuex,项目必须也得支持promise,store也必须通过new来创建实例if (process.env.NODE_ENV !== 'production') {assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)assert(this instanceof Store, `Store must be called with the new operator.`)}// 从参数options中结构出相关变量const {plugins = [],strict = false} = optionslet {state = {}} = options// 这个简单的,不解释if (typeof state === 'function') {state = state()}// store internal state// 初始化store内部状态,Object.create(null)可以创建一个干净的空对象this._committing = falsethis._actions = Object.create(null)this._mutations = Object.create(null)this._wrappedGetters = Object.create(null)// vuex支持模块,即将state通过key-value的形式拆分为多个模块// 模块的具体内容可以查看这里 :https://vuex.vuejs.org/en/mutations.htmlthis._modules = new ModuleCollection(options)this._modulesNamespaceMap = Object.create(null)// 监听队列,当执行commit时会执行队列中的函数this._subscribers = []// 创建一个vue实例,利用vue的watch的能力,可以监控state的改变,具体后续watch方法会介绍this._watcherVM = new Vue()// bind commit and dispatch to selfconst store = this// 缓存dispatch和commit方法const { dispatch, commit } = this// 定义dispatch方法this.dispatch = function boundDispatch (type, payload) {return dispatch.call(store, type, payload)}// 定义commit方法this.commit = function boundCommit (type, payload, options) {return commit.call(store, type, payload, options)}// strict mode// 定义严格模式,不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。// 具体后续enableStrictMode方法会提到this.strict = strict// init root module.// this also recursively registers all sub-modules// and collects all module getters inside this._wrappedGetters// 这个作者的注释已经写得挺明白,就是初始化根模块,递归注册子模块,收集getterinstallModule(this, state, [], this._modules.root)// initialize the store vm, which is responsible for the reactivity// (also registers _wrappedGetters as computed properties)// 初始化store中的state,使得state变成响应式的,原理就是将state作为一个vue实例的data属性传入,具体在分析这个函数的时候会介绍resetStoreVM(this, state)// apply plugins// 执行插件,这个是一个数组,所以遍历他,然后执行每个插件的函数plugins.concat(devtoolPlugin).forEach(plugin => plugin(this))}
呼呼呼~ 至此,终于把store类全部读完了,休息五分钟,然后继续往下看哈。
接下来关于state的获取和设置
// 获取state, 直接返回内部data的$$stateget state () {return this._vm._data.$$state}set state (v) {if (process.env.NODE_ENV !== 'production') {assert(false, `Use store.replaceState() to explicit replace store state.`)}}
commit是vuex中一个比较重要的操作,因为它可以触发mutation修改对state的修改,并且是同步执行的
commit (_type, _payload, _options) {// check object-style commit// 首先统一传入参数的格式,主要是针对当type是个对象的情况,需要把这个对象解析出来const {type,payload,options} = unifyObjectStyle(_type, _payload, _options)// 缓存本次commit操作的类型和负荷,以供后续监听队列(this._subscribers)使用const mutation = { type, payload }// 获取相关的type的mutation函数,我们都知道,在vuex中都是通过commit一个类型然后触发相关的mutation函数来操作state的,所以在此必须获取相关的函数const entry = this._mutations[type]if (!entry) {if (process.env.NODE_ENV !== 'production') {console.error(`[vuex] unknown mutation type: ${type}`)}return}// 在_withCommit中触发上面获取的mutation函数,简单粗暴使用数组的forEach执行哈哈,之所以要在外面包一层_withCommit,是表明操作的同步性this._withCommit(() => {entry.forEach(function commitIterator (handler) {handler(payload)})})// 这个就是之前说的监听队列,在每次执行commit函数时都会遍历执行一下这个队列this._subscribers.forEach(sub => sub(mutation, this.state))if (process.env.NODE_ENV !== 'production' &&options && options.silent) {console.warn(`[vuex] mutation type: ${type}. Silent option has been removed. ` +'Use the filter functionality in the vue-devtools')}}
dispatch是跟commit有点相似的函数,但是commit必须是同步的,而dispatch是异步的,内部还是必须通过commit来操作state
dispatch (_type, _payload) {// check object-style dispatch// 同上面commit,不解释const {type,payload} = unifyObjectStyle(_type, _payload)// 因为dispatch触发的是actions中的函数,所以这里获取actions相关函数,过程类似commitconst entry = this._actions[type]if (!entry) {if (process.env.NODE_ENV !== 'production') {console.error(`[vuex] unknown action type: ${type}`)}return}// 因为dispatch支持异步,所以这里作者使用Promise.all来执行异步函数并且判断所有异步函数是否都已经执行完成,所以在文件最开始判断了当前环境必须支持promise就是这个原因return entry.length > 1? Promise.all(entry.map(handler => handler(payload))): entry[0](payload)}
subscribe函数,这是pub/sub模式在vuex中的一个运用,用户可以通过subscribe函数来监听state的变化,函数返回一个取消监听的函数,便于用户在合适的时机取消订阅
subscribe (fn) {const subs = this._subscribersif (subs.indexOf(fn) < 0) {subs.push(fn)
}// 返回取消订阅的函数,通过函数额splice方法,来清除函数中不需要的项return () => {const i = subs.indexOf(fn)
if (i > -1) {subs.splice(i, 1)}}}
watch函数,响应式地监测一个 getter 方法的返回值,当值改变时调用回调函数,原理其实就是利用vue中的watch方法
watch (getter, cb, options) {if (process.env.NODE_ENV !== 'production') {assert(typeof getter === 'function', `store.watch only accepts a function.`)}// 在上面构造函数中,我们看到this._watcherVM就是一个vue的实例,所以可以利用它的watch来实现vuex的watch,原理都一样,当监听的值或者函数的返回值发送改变的时候,就触发相应的回调函数,也就是我们传入的cb参数,options则可以来让监听立即执行&深度监听对象return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)}
replaceState,根据名字就可知道,是替换当前的state
replaceState (state) {this._withCommit(() => {this._vm._data.$$state = state})}
registerModule函数,可以使用 store.registerModule 方法注册模块
registerModule (path, rawModule) {if (typeof path === 'string') path = [path]if (process.env.NODE_ENV !== 'production') {assert(Array.isArray(path), `module path must be a string or an Array.`)assert(path.length > 0, 'cannot register the root module by using registerModule.')}//其实内部时候通过,register方法,递归寻找路径,然后将新的模块注册root模块上,具体后续介绍到module的时候会详细分析this._modules.register(path, rawModule)//安装模块,因为每个模块都有他自身的getters,actions, modules等,所以,每次注册模块都必须把这些都注册上,后续介绍installModule的时候,会详细介绍到installModule(this, this.state, path, this._modules.get(path))// reset store to update getters...// 重置VMresetStoreVM(this, this.state)}
unregisterModule函数,上述registerModule函数的相反操作,具体在module的时候会介绍到,在此了解个大概,先不纠结细节
unregisterModule (path) {if (typeof path === 'string') path = [path]if (process.env.NODE_ENV !== 'production') {assert(Array.isArray(path), `module path must be a string or an Array.`)}this._modules.unregister(path)this._withCommit(() => {const parentState = getNestedState(this.state, path.slice(0, -1))// 利用vue.delete方法,确保模块在被删除的时候,视图能监听到变化Vue.delete(parentState, path[path.length - 1])})resetStore(this)}
hotUpdate函数,Vuex 支持在开发过程中热重载 mutation、modules、actions、和getters
hotUpdate (newOptions) {this._modules.update(newOptions)resetStore(this, true)}
_withCommit函数,从函数名可以看出这是一个内部方法,作用就是保证commit过程中执行的方法都是同步的
_withCommit (fn) {// 保存原来的committing的状态const committing = this._committing//将想在的committing状态设置为truethis._committing = true//执行函数fn()//将committing状态设置为原来的状态this._committing = committing}
到目前为止,我们已经看完了Store这个类的所有代码~慢慢消化,然后继续往下
----------------------------------------------------------这又是分割线----------------------------------------------------------------------------------------
接下来,我们分析一下,一些其他的辅助方法,跟上面store的一些内容会有相关。ready? Go
resetStore函数,用于重置整个vuex中的store,从代码中可以看出,这个函数主要的功能,就是将传入的store实例的_actions,_mutations,_wrappedGetters,_modulesNamespaceMap置为空,然后重新安装模块和重置VM,此方法在上述热更新和注销模块的时候会使用到
function resetStore (store, hot) {store._actions = Object.create(null)store._mutations = Object.create(null)store._wrappedGetters = Object.create(null)store._modulesNamespaceMap = Object.create(null)const state = store.state// init all modulesinstallModule(store, state, [], store._modules.root, true)// reset vmresetStoreVM(store, state, hot)
}
resetStoreVM函数,这个用于重置store中的vm,所谓vm,指的就是视图模型,也就是常见mvvm中的vm,在此指的是将state作为data中$$state属性的一个vue实例
function resetStoreVM (store, state, hot) {// 保存原有store的_vmconst oldVm = store._vm// bind store public gettersstore.getters = {}// store的_wrappedGetters缓存了当前store中所有的getterconst wrappedGetters = store._wrappedGettersconst computed = {}//遍历这个对象,获取每个getter的key和对应的方法forEachValue(wrappedGetters, (fn, key) => {// use computed to leverage its lazy-caching mechanism// 将getter以key-value的形式缓存在变量computed中,其实后面就是将getter作为vue实例中的计算属性computed[key] = () => fn(store)// 当用户获取getter时,相当于获取vue实例中的计算属性,使用es5的这个Object.defineProperty方法做一层代理Object.defineProperty(store.getters, key, {get: () => store._vm[key],enumerable: true // for local getters})})// use a Vue instance to store the state tree// suppress warnings just in case the user has added// some funky global mixinsconst silent = Vue.config.silent// silent设置为true,则取消了所有的警告和日志,眼不见为净Vue.config.silent = true// 将传入的state,作为vue实例中的data的$$state属性,将刚刚使用computed变量搜集的getter,作为实例的计算属性,所以当state和getter都变成了响应式的了store._vm = new Vue({data: {$$state: state},computed})Vue.config.silent = silent// enable strict mode for new vmif (store.strict) {//如果设置了严格模式则,不允许用户在使用mutation以外的方式去修改stateenableStrictMode(store)}if (oldVm) {if (hot) {// dispatch changes in all subscribed watchers// to force getter re-evaluation for hot reloading.store._withCommit(() => {// 将原有的vm中的state设置为空,所以原有的getter都会重新计算一遍,利用的就是vue中的响应式,getter作为computed属性,只有他的依赖改变了,才会重新计算,而现在把state设置为null,所以计算属性重新计算oldVm._data.$$state = null})}// 在下一次周期销毁实例Vue.nextTick(() => oldVm.$destroy())}
}
installModule函数,用于安装模块,注册相应的mutation,action,getter和子模块等
function installModule (store, rootState, path, module, hot) {//判断是否为根模块const isRoot = !path.length//根据路径生成相应的命名空间const namespace = store._modules.getNamespace(path)// register in namespace mapif (module.namespaced) {store._modulesNamespaceMap[namespace] = module}// set stateif (!isRoot && !hot) {// 将模块的state设置为响应式const parentState = getNestedState(rootState, path.slice(0, -1))const moduleName = path[path.length - 1]store._withCommit(() => {Vue.set(parentState, moduleName, module.state)})}//设置本地上下文,主要是针对模块的命名空间,对dispatch,commit,getters和state进行修改,后面讲到makeLocalContext的时候会详细分析,现在只需要知道,这个操作让用户能够直接获取到对象子模块下的对象就可以了const local = module.context = makeLocalContext(store, namespace, path)//将mutation注册到模块上module.forEachMutation((mutation, key) => {const namespacedType = namespace + keyregisterMutation(store, namespacedType, mutation, local)})//将action注册到模块上 module.forEachAction((action, key) => {const namespacedType = namespace + keyregisterAction(store, namespacedType, action, local)})//将getter注册到模块上module.forEachGetter((getter, key) => {const namespacedType = namespace + keyregisterGetter(store, namespacedType, getter, local)})//递归安装子模块 module.forEachChild((child, key) => {installModule(store, rootState, path.concat(key), child, hot)})
}
makeLocalContext函数,就是installModule中设置本地上下文的具体实现
function makeLocalContext (store, namespace, path) {//如果没有命名空间,则是使用全局store上的属性,否则对store上的属性进行本地化处理const noNamespace = namespace === ''const local = {dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {//dispatch的本地化处理,就是修改typeconst args = unifyObjectStyle(_type, _payload, _options)const { payload, options } = argslet { type } = argsif (!options || !options.root) {//在type前面加上命名空间type = namespace + typeif (process.env.NODE_ENV !== 'production' && !store._actions[type]) {console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)return}}//调用store上的dispatch方法return store.dispatch(type, payload)},commit: noNamespace ? store.commit : (_type, _payload, _options) => {// commit的本地化修改跟dispatch相似,也是只是修改了type,然后调用store上面的commitconst args = unifyObjectStyle(_type, _payload, _options)const { payload, options } = argslet { type } = argsif (!options || !options.root) {type = namespace + typeif (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)return}}store.commit(type, payload, options)}}// getters and state object must be gotten lazily// because they will be changed by vm update//gettters和state的修改,则依赖于makeLocalGetters函数和getNestedState函数,后面会分析Object.defineProperties(local, {getters: {get: noNamespace? () => store.getters: () => makeLocalGetters(store, namespace)},state: {get: () => getNestedState(store.state, path)}})return local
}
makeLocalGetters函数,则是对getter进行本地化处理
function makeLocalGetters (store, namespace) {const gettersProxy = {}const splitPos = namespace.lengthObject.keys(store.getters).forEach(type => {//这里获取的每个type都是一个有命名空间+本地type的字符串,例如: type的值可能为 “m1/m2/”+"typeName"// skip if the target getter is not match this namespaceif (type.slice(0, splitPos) !== namespace) return// extract local getter typeconst 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.//相当于做了一层代理,将子模块的localType映射到store上的typeObject.defineProperty(gettersProxy, localType, {get: () => store.getters[type],enumerable: true})})return gettersProxy
}
registerMutation函数,就是注册mutation的过程,将相应type的mutation推到store._mutations[type]的队列中,当commit这个type的时候就触发执行队列中的函数
function registerMutation (store, type, handler, local) {const entry = store._mutations[type] || (store._mutations[type] = [])entry.push(function wrappedMutationHandler (payload) {handler(local.state, payload)})
}
registerAction函数,注册action的过程,原理类似于registerMutation,不同点在于action支持异步,所以必须用promise进行包装
function registerAction (store, type, handler, local) {const entry = store._actions[type] || (store._actions[type] = [])entry.push(function wrappedActionHandler (payload, cb) {let res = handler({dispatch: local.dispatch,commit: local.commit,getters: local.getters,state: local.state,rootGetters: store.getters,rootState: store.state}, payload, cb)if (!isPromise(res)) {res = Promise.resolve(res)}if (store._devtoolHook) {return res.catch(err => {store._devtoolHook.emit('vuex:error', err)throw err})} else {return res}})
}
registerGetters函数,根据type,将getter方法挂载在store._wrappedGetters[type]下面
function registerGetter (store, type, rawGetter, local) {if (store._wrappedGetters[type]) {if (process.env.NODE_ENV !== 'production') {console.error(`[vuex] duplicate getter key: ${type}`)}return}store._wrappedGetters[type] = function wrappedGetter (store) {// 为子模块的getter提供了这个四个参数,方便用户获取,如果是根模块,则local跟store取出来的state和getters相同return rawGetter(local.state, // local statelocal.getters, // local gettersstore.state, // root statestore.getters // root getters)}
}
enableStrictMode函数则是在严格模式下,不允许state被除mutation之外的其他操作修改,代码比较简单,利用vue的$watch方法实现的
function enableStrictMode (store) {store._vm.$watch(function () { return this._data.$$state }, () => {if (process.env.NODE_ENV !== 'production') {assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`)}}, { deep: true, sync: true })
}
getNestedState函数,获取对应路径下的state
function getNestedState (state, path) {return path.length? path.reduce((state, key) => state[key], state): state
}
unifyObjectStyle函数,作用是调整参数,主要是当type是一个对象的时候,对参数进行调整
function unifyObjectStyle (type, payload, options) {if (isObject(type) && type.type) {options = payloadpayload = typetype = type.type}if (process.env.NODE_ENV !== 'production') {assert(typeof type === 'string', `Expects string as the type, but found ${typeof type}.`)}return { type, payload, options }
}
以上是相关辅助函数的全部内容,你看明白了么~
----------------------------------------------------------这依然是分割线------------------------------------------------------------------------------------
文件的最后,就是定义了install函数,然后自动执行了这个函数,让vuex能够在项目中运作起来
export function install (_Vue) {if (Vue) {if (process.env.NODE_ENV !== 'production') {console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.')}return}Vue = _Vue//在vue的生命周期中初始化vuex,具体实现后面讲到mixin.js这个文件时会说明applyMixin(Vue)
}// auto install in dist mode
if (typeof window !== 'undefined' && window.Vue) {install(window.Vue)
}
以上就是store.js的所有内容啦~
----------------------------------------------------------严肃分割线------------------------------------------------------------------------------------
接下来讲解有关module目录下的内容,该目录有两个文件分别是module-collection.js和module.js,这两个文件主要是有关于vuex中模块的内容;
首先我们看看module-collection.js,这个文件主要导出一个ModuleCollection类:
构造函数
constructor (rawRootModule) {// register root module (Vuex.Store options)//主要是注册根模块,我们在之前store的构造函数中曾经使用到 this._modules = new ModuleCollection(options),注册一个根模块然后缓存在this._module中this.register([], rawRootModule, false)}
紧接着看看下面register函数,它用于注册模块
register (path, rawModule, runtime = true) {if (process.env.NODE_ENV !== 'production') {assertRawModule(path, rawModule)}// 创建一个新模块,具体会在后面讲到Module的时候分析const newModule = new Module(rawModule, runtime)// 判读是否为根模块if (path.length === 0) {this.root = newModule} else {//根据path路径,利用get方法获取父模块 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)})}}
相反,unregister函数则是移除一个模块
unregister (path) {// 通过get方法获取父模块const parent = this.get(path.slice(0, -1))//获取需要删除的模块的名称,即他的keyconst key = path[path.length - 1]if (!parent.getChild(key).runtime) return//利用module中removeChild方法删除该模块,其实就是delete了对象上的一个keyparent.removeChild(key)}
get函数,其实就是利用es5中数组reduce方法,从根模块开始根据传入的path来获取相应的子模块
get (path) {return path.reduce((module, key) => {return module.getChild(key)}, this.root)}
getNamespace函数,利用传入的参数path,生成相应的命名空间,实现的原理跟上述的get方法类似
getNamespace (path) {let module = this.rootreturn path.reduce((namespace, key) => {module = module.getChild(key)return namespace + (module.namespaced ? key + '/' : '')}, '')}
upate方法,就是更新模块,具体看下面update方法的实现
update (rawRootModule) {update([], this.root, rawRootModule)}
以上就是整个ModuleCollection类的实现
接下来讲解一下function update的实现
function update (path, targetModule, newModule) {if (process.env.NODE_ENV !== 'production') {assertRawModule(path, newModule)}// update target module//目标模块更新为新模块,具体实现是将原有模块的namespaced,actions,mutations,getters替换为新模块的namespaced,actions,mutations,getters// 具体会在Module类中update方法讲解targetModule.update(newModule)// update nested modules// 如果新的模块有子模块,则递归更新子模块if (newModule.modules) {for (const key in newModule.modules) {if (!targetModule.getChild(key)) {if (process.env.NODE_ENV !== 'production') {console.warn(`[vuex] trying to add a new module '${key}' on hot reloading, ` +'manual reload is needed')}return}update(path.concat(key),targetModule.getChild(key),newModule.modules[key])}}
}
至于assertRawModule方法和makeAssertionMessage方法,就是一些简单的校验和提示,不影响主流程&代码比较简单,这里不做赘述
以上就是整个module-collection.js文件的所有内容
接下来就应该分析目录中的另一个文件module.js,这个文件主要导出一个Module类,这个类主要描述了vuex中模块的功能
构造函数,主要做了一些模块初始化的事情
//构造函数,主要做了一些模块初始化的事情constructor (rawModule, runtime) {//缓存运行时的标志this.runtime = runtime//创建一个空对象来保存子模块this._children = Object.create(null)//缓存传入的模块this._rawModule = rawModule//缓存传入模块的state,如果state是一个函数,则执行这个函数const rawState = rawModule.statethis.state = (typeof rawState === 'function' ? rawState() : rawState) || {}}
namespaced函数是主要就是获取当前模块是否是命名模块,vuex支持命名模块和匿名模块
get namespaced () {return !!this._rawModule.namespaced}
addChild,removeChild,getChild这三个函数就分别是添加,删除,获取子模块,内容比较简单,不赘述
update方法,将原有缓存模块的namespaced,actions,mutations,getters替换成新传入模块的namespaced,actions,mutations,getters
update (rawModule) {this._rawModule.namespaced = rawModule.namespacedif (rawModule.actions) {this._rawModule.actions = rawModule.actions}if (rawModule.mutations) {this._rawModule.mutations = rawModule.mutations}if (rawModule.getters) {this._rawModule.getters = rawModule.getters}}
forEachChild函数,利用util中forEachValue方法,变量每个子模块,将每个子模块作为传入的回调函数参数,然后执行回调函数
forEachChild (fn) {forEachValue(this._children, fn)}
forEachGetter,forEachAction,forEachMutation代码逻辑跟上述forEachChild十分类似,不在赘述
以上就是module.js文件的所有内容,至此我们也已经全部分析完module目录下的所有代码了
---------------------------------------------------一本正经分割线--------------------------------------------------------------------------------
接下来,我们再看看help.js这个文件,这个文件主要是提供了一些帮助性的方法,使得用户在使用vuex的过程中体验更好,更加方便
首先我们先看看文件后面三个函数:normalizeMap,normalizeNamespace,getModuleByNamespace
normalizeMap函数,这个方法的作用是格式化传入的对象
function normalizeMap (map) {// 如果传入的对象是数组,则放回一个每一项都是key-val对象的数组,其中key和val的值相同// 如果出入的是一个对象,则变量这个对象,放回一个每一项都是key-val数组,其中key对应对象的key,val对应相应key的值return Array.isArray(map)? map.map(key => ({ key, val: key })): Object.keys(map).map(key => ({ key, val: map[key] }))
}
normalizeNamespace函数,调整参数,格式化命名空间
function normalizeNamespace (fn) {return (namespace, map) => {//如果没传入命名空间,或者传入的命名空间不是一个字符串,则丢弃该参数if (typeof namespace !== 'string') {map = namespacenamespace = ''} else if (namespace.charAt(namespace.length - 1) !== '/') {//否则判断命名空间后面是否有加上‘/’,如果没有则加上namespace += '/'}//最后执行传入的回调函数return fn(namespace, map)}
}
getModuleByNamespace函数,通过命名空间来获取模块
function getModuleByNamespace (store, helper, namespace) {// 返回store._modulesNamespaceMap缓存的模块const module = store._modulesNamespaceMap[namespace]if (process.env.NODE_ENV !== 'production' && !module) {console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)}return module
}
mapState函数,我们可以通过这个方法将state解构到vue项目中去,使其变成vue实例中的计算属性
export const mapState = normalizeNamespace((namespace, states) => {//定义一个空对象const res = {}normalizeMap(states).forEach(({ key, val }) => {//收集states的所有key,对应key的值,改变成一个mappedState方法,符合计算属性的特点res[key] = function mappedState () {//获取store的state和getterslet state = this.$store.statelet getters = this.$store.getters//如果存在命名空间,则将命名空间下子模块的state和getters覆盖原来store的state和gettersif (namespace) {const module = getModuleByNamespace(this.$store, 'mapState', namespace)if (!module) {return}state = module.context.stategetters = module.context.getters}//如果对应的val是函数则执行,否则返回state下的值return typeof val === 'function'? val.call(this, state, getters): state[val]}// mark vuex getter for devtoolsres[key].vuex = true})//返回这个包装过state的对象,这个对象可以结构成vue中的计算属性return res
})
mapMutations函数,则是将mutation解构到vue实例中的methods中,使得用户可以直接调用methods中的方法来执行store.commit
export const mapMutations = normalizeNamespace((namespace, mutations) => {//定义一个空对象const res = {}normalizeMap(mutations).forEach(({ key, val }) => {val = namespace + valres[key] = function mappedMutation (...args) {if (namespace && !getModuleByNamespace(this.$store, 'mapMutations', namespace)) {return}//调用了store中的commit方法,触发相应的mutation函数的执行return this.$store.commit.apply(this.$store, [val].concat(args))}})return res
})
mapGetters的逻辑跟mapState类似,mapActions的逻辑跟mapMutations类似,这里不再赘述
自此,我们把help.js的内容也分析完了
转载https://www.talkingcoder.com/article/6443740048396582923