持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情
前言
接上篇文章vuex实现原理commit,在上篇文章分享中我们解读了Vuex中Store类的构造函数,安装模块函数installModule、注册mutation函数registerMutation和提交函数commit的源码,知道了Store实例化时都做了哪些事,了解了当我们调用this.$store.commit时在底层都做了什么操作。然而这也只是vuex其中的一部分,我们知道除了这些其实vuex中还有action和getter等对象,那么接下来我们再来看看它们又是如何工作的。
registerAction
在前面分析registerMutation时我们知道,其主要就是给对象_mutations中添加一个以type参数值(mutation中的方法)命名的属性,对应的值是一个数组,并向数组中添加了一个wrappedMutationHandler的函数,而在wrappedMutationHandler内部则会让我们自己定义的那个mutation中的方法执行。其实这个registerAction做的事情也差不多
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload)
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
}
})
}
- 首先registerAction接收4个参数:store Store实例,type 在actions对象中定义的方法名, handler actions对象中定义的具体的方法,local 包含了dispatch/commit等信息等对象
- 在方法体内首先还是从_actions对象中看看有没有方法名对应的属性
- 如果有值则把值(数组)赋给entry变量
- 如果没有值则给对象_actions添加一个以方法名命名的属性并赋值为空数组
- 然后向数组中添加一个方法wrappedActionHandler
- 在该方法中主要做的事情就是让我们在actions对象中定义的方法执行
handler.call
- 关于传递的四个参数:第一个参数store是Store实例,目的是让handler方法内部的this指向store
- 第二个参数则对应着handler方法的第一个参数,其实也是个store
- 第三个参数payload是需要传递给handler的参数
- 在该方法中主要做的事情就是让我们在actions对象中定义的方法执行
- 将handler方法的返回值返回出去
- 如果返回值不是promise,则需要用promise.resolve转换成promise再返回,如果是promise则直接返回
- 我们前面提到过,mutation中的方法用来修改state中的属性值,并且mutation中的方法必须是同步方法
- 而action中的方法其实也是用来修改state中的属性值的,只不过是这里的方法是异步操作,所以这里要返回个promise 下面我们再来看下当我们通过dispatch方法去执行action中的异步方法时都做了哪些事情
dispatch
dispatch (_type, _payload) {
// check object-style dispatch
const { type, payload } = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
this._actionSubscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.filter(sub => sub.before)
.forEach(sub => sub.before(action, this.state))
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
return result.then(res => {
this._actionSubscribers
.filter(sub => sub.after)
.forEach(sub => sub.after(action, this.state))
}
return res
})
}
- dispatch方法接收两个参数:_type 是actions对象中我们自己定义的方法, _payload 是_type方法需要的参数
- 方法内部首先还是通过unifyObjectStyle方法,跟commit方法中的作用一样,也是将方法名和参数区分开来,因为dispatch也是可用传递一个对象作为参数的
- 接下来在_actions对象中找到之前在registerAction时添加的wrappedActionHandler
- 在执行entry数组中的方法之前,这里还会执行一些额外的操作:就是遍历_actionSubscribers,让该数组中所有的对象的before方法执行,对应的还有after方法
- 这里就又涉及到了一个subscribeAction方法,该方法也是挂载在Store原型对象上的一个方法,主要用来在执行dispatch中的方法前或者dispatch方法执行完成后做一些操作
- 如果外面通过store实例调用subscribeAction时传的是before,这些before方法就会在这里执行
- 如果外面传递的是after,那么就会在所有的promise执行完成后再调用,在dispatch方法的最下面也能看到
- 如果调用subscribeAction时不传after也不传before 则默认是before
- 然后再判断如果entry中有多个方法(promise)则通过Promise.all来进行管理执行,否则就直接调用即可
- 最后还是返回一个promise,并在promise的then中仍然遍历_actionSubscribers数组,目的是让subscribeAction执行时传递的那些after方法执行
总结
本次分享我们对registerAction和dispatch方法的源码进行了解读,简单总结:
在注册action时向数组中添加一个wrappedActionHandler方法,并在该方法中让我们在actions对象中自定义的那个方法执行,最后返回一个promise。然后当我们调用dispatch时就会从_actions对象中找到注册时保存好的方法,如果是多个就利用Promise.all进行管理执行,如果只有一个方法则直接执行,最后仍然返回的是promise。总之就是为了让我们的方法异步执行
好了本次源码解读就先到这里了。