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
方法。