vuex工具函数原理解读
不论是vuex3还是vuex4在工具函数这一部分的主要原理是不变的,即可以不去特别分开理解。
在理解工具函数原理前先解读一下store对象上重要的属性:
js
// store internal state
//属性是这些,但是getters和state的初始化不是在这进行
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._makeLocalGettersCache = Object.create(null)
this.getters = {}
this.state = {}
工具函数中主要访问到的属性是_modulesNamespaceMap、state以及getters。
这些工具函数中都用到了三个函数,先来看这三个函数内容吧:
js
function normalizeNamespace (fn) {
return (namespace, map) => {
if (typeof namespace !== 'string') {
map = namespace
namespace = ''
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/'
}
return fn(namespace, map)
}
}
/**
* Normalize the map
* normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
* normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
* @param {Array|Object} map
* @return {Object}
*/
function normalizeMap (map) {
if (!isValidMap(map)) {
return []
}
return Array.isArray(map)
? map.map(key => ({ key, val: key }))
: Object.keys(map).map(key => ({ key, val: map[key] }))
}
function getModuleByNamespace (store, helper, namespace) {
const module = store._modulesNamespaceMap[namespace]
if (__DEV__ && !module) {
console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)
}
return module
}
-
normalizeNamespace:包装接收到的函数,使namespace是空字符串或者moduleName/的格式,例如user/,使map是一个数组或者对象。 -
normalizeMap:让map是一个装着特定对象的数组。tsinterface obj { key: any val: any } type map = obj[] -
getModuleByNamespace:获取_moduelsNamespaceMap属性下的模块。
mapState源码:
js
export const mapState = normalizeNamespace((namespace, states) => {
const res = {}
if (__DEV__ && !isValidMap(states)) {
console.error('[vuex] mapState: mapper parameter must be either an Array or an Object')
}
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState () {
let state = this.$store.state
let getters = this.$store.getters
if (namespace) {
const module = getModuleByNamespace(this.$store, 'mapState', namespace)
if (!module) {
return
}
state = module.context.state
getters = module.context.getters
}
return typeof val === 'function'
? val.call(this, state, getters)
: state[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
可以看出两种使用该方法的方式:
- 使用子模块的属性方法
js
mapState('moduleName/',['state1','state2'])
- 使用
$store的属性方法
js
mapState(['state1','state2'])
而这个方法返回的一个所有属性都是一个函数的对象,每个属性函数都有一个返回值,这也就是为什么mapState是写在computed中作为计算属性的原因。
mapMutations:
js
export const mapMutations = normalizeNamespace((namespace, mutations) => {
const res = {}
if (__DEV__ && !isValidMap(mutations)) {
console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object')
}
normalizeMap(mutations).forEach(({ key, val }) => {
res[key] = function mappedMutation (...args) {
// Get the commit method from store
let commit = this.$store.commit
if (namespace) {
const module = getModuleByNamespace(this.$store, 'mapMutations', namespace)
if (!module) {
return
}
commit = module.context.commit
}
return typeof val === 'function'
? val.apply(this, [commit].concat(args))
: commit.apply(this.$store, [val].concat(args))
}
})
return res
})
mapGetters:
js
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {}
if (__DEV__ && !isValidMap(getters)) {
console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object')
}
normalizeMap(getters).forEach(({ key, val }) => {
// The namespace has been mutated by normalizeNamespace
val = namespace + val
res[key] = function mappedGetter () {
if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
return
}
if (__DEV__ && !(val in this.$store.getters)) {
console.error(`[vuex] unknown getter: ${val}`)
return
}
return this.$store.getters[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
mapActions:
js
export const mapActions = normalizeNamespace((namespace, actions) => {
const res = {}
if (__DEV__ && !isValidMap(actions)) {
console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object')
}
normalizeMap(actions).forEach(({ key, val }) => {
res[key] = function mappedAction (...args) {
// get dispatch function from store
let dispatch = this.$store.dispatch
if (namespace) {
const module = getModuleByNamespace(this.$store, 'mapActions', namespace)
if (!module) {
return
}
dispatch = module.context.dispatch
}
return typeof val === 'function'
? val.apply(this, [dispatch].concat(args))
: dispatch.apply(this.$store, [val].concat(args))
}
})
return res
})
总结
mapXXX工具函数都是通过normalizeNamespace接收一个fn函数返回一个包装函数wrapFn,而wrapFn返回的是一个全部属性都是函数的对象。
mapState:读取$store或者module.context.state上的属性,返回一个属性函数对象 ,每个属性都有一个返回值,这个返回值即指定state的值。mapMutations:读取$store上的commit方法或者module.context上的commit方法。mapGetters:读取$store上的getters属性。mapActions:读取$store上的dispatch方法或者module.context上的dispatch方法。