Vuex 源码阅读系列会分为三篇,前两篇会主要解析 Vuex 初始化中做了什么,最后一篇主要解析各种常用 API。
因为涉及代码较多,更推荐在 PC 浏览。
常用 API
commit 解析
如果需要改变状态的话,一般都会使用 commit
去操作,接下来让我们来看看 commit
是如何实现状态的改变的
commit(_type, _payload, _options) {
// 检查传入的参数
const { type, payload, options } = unifyObjectStyle(
_type,
_payload,
_options
)
const mutation = { type, payload }
// 找到对应的 mutation 函数
const entry = this._mutations[type]
// 判断是否找到
if (!entry) {
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
// _withCommit 函数将 _committing
// 设置为 TRUE,保证在 strict 模式下
// 只能 commit 改变状态
this._withCommit(() => {
entry.forEach(function commitIterator(handler) {
// entry.push(function wrappedMutationHandler(payload) {
// handler.call(store, local.state, payload)
// })
// handle 就是 wrappedMutationHandler 函数
// wrappedMutationHandler 内部就是调用
// 对于的 mutation 函数
handler(payload)
})
})
// 执行订阅函数
this._subscribers.forEach(sub => sub(mutation, this.state))
}
dispatch 解析
如果需要异步改变状态,就需要通过 dispatch 的方式去实现。在 dispatch 调用的 commit
函数都是重写过的,会找到模块内的 mutation 函数。
dispatch(_type, _payload) {
// 检查传入的参数
const { type, payload } = unifyObjectStyle(_type, _payload)
const action = { type, payload }
// 找到对于的 action 函数
const entry = this._actions[type]
// 判断是否找到
if (!entry) {
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
// 触发订阅函数
this._actionSubscribers.forEach(sub => sub(action, this.state))
// 在注册 action 的时候,会将函数返回值
// 处理成 promise,当 promise 全部
// resolve 后,就会执行 Promise.all
// 里的函数
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
各种语法糖
在组件中,如果想正常使用 Vuex 的功能,经常需要这样调用 this.$store.state.xxx
的方式,引来了很多的不便。为此,Vuex 引入了语法糖的功能,让我们可以通过简单的方式来实现上述的功能。以下以 mapState
为例,其他的几个 map 都是差不多的原理,就不一一解析了。
function normalizeNamespace(fn) {
return (namespace, map) => {
// 函数作用很简单
// 根据参数生成 namespace
if (typeof namespace !== 'string') {
map = namespace
namespace = ''
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/'
}
return fn(namespace, map)
}
}
// 执行 mapState 就是执行
// normalizeNamespace 返回的函数
export const mapState = normalizeNamespace((namespace, states) => {
const res = {}
// 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 } ]
// function normalizeMap(map) {
// return Array.isArray(map)
// ? map.map(key => ({ key, val: key }))
// : Object.keys(map).map(key => ({ key, val: map[key] }))
// }
// states 参数可以参入数组或者对象类型
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
}
// 返回 State
return typeof val === 'function'
? val.call(this, state, getters)
: state[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
最后
以上是第三部分的 Vuex 源码解析,介绍了 Vuex 常用 API 的原理知识。