const
{
type,
payload
} =
unifyObjectStyle
(_type, _payload)
const
action = {
type,
payload
const
entry =
this
.
_actions
[type]
if
(!entry) {
if
(__DEV__) {
console
.
error
(
`[vuex] unknown action type:
${type}
`
)
return
try
{
this
.
_actionSubscribers
.
slice
()
.
filter
(
sub
=>
sub.
before
)
.
forEach
(
sub
=>
sub.
before
(action,
this
.
state
))
}
catch
(e) {
if
(__DEV__) {
console
.
warn
(
`[vuex] error in before action subscribers: `
)
console
.
error
(e)
const
result = entry.
length
>
1
?
Promise
.
all
(entry.
map
(
handler
=>
handler
(payload))) : entry[
0
](payload)
return
new
Promise
(
(
resolve, reject
) =>
{
result.
then
(
res
=>
{
try
{
this
.
_actionSubscribers
.
filter
(
sub
=>
sub.
after
)
.
forEach
(
sub
=>
sub.
after
(action,
this
.
state
))
}
catch
(e) {
if
(__DEV__) {
console
.
warn
(
`[vuex] error in after action subscribers: `
)
console
.
error
(e)
resolve
(res)
},
error
=>
{
try
{
this
.
_actionSubscribers
.
filter
(
sub
=>
sub.
error
)
.
forEach
(
sub
=>
sub.
error
(action,
this
.
state
, error))
}
catch
(e) {
if
(__DEV__) {
console
.
warn
(
`[vuex] error in error action subscribers: `
)
console
.
error
(e)
reject
(error)
为了更好的理解源码中对this._actionSubscribers的操作,我们需要了解一下Vuex.Store的实例方法:
subscribeAction
。
学习这个方法时,我们可以看到对其作用的定义:
它是用来订阅store的action。handler会在每个action分发的时候调用并接收action描述和当前的store的state这两个参数
若是,对这段话不甚清楚的话,可以多看几遍官方的举例:
store.subscribeAction((action, state) => {
console.log(action.type)
console.log(action.payload)
handler指的就是传入subscribeAction方法的函数。
能接收action描述和当前的store的state这两个参数,是因为在对this._actionSubscribers进行遍历时就已传入。
光靠文字描述,还不能够太清晰。所以,我们就结合案例及其源码来看一下吧。
subscribeAction 案例
所用案例,是vuex项目中提供的counter测试案例(目录位置:examples/counter)。
调用store实例方法 dispatch 和 subscribeAction:
参数打印:
图中,我们不仅打印了传入的参数,也打印了this._actionSubscribers。初始时,它是个空数组, 一旦调用store.subscribeAction,这个数组中就会被放入一个函数,就是传入subscribeAction的handler。
subscribeAction 源码
subscribeAction (fn, options) {
const subs = typeof fn === 'function' ? {
before: fn
} : fn
return genericSubscribe(subs, this._actionSubscribers, options)
如果fn是个函数,就将一个对象赋值给subs,此对象,以before为键,fn为值。若是,你有仔细阅读vuex中有关subscribeAction的教程的话,想必你已明白,这么做的原由,也就是下面这句话:
从 3.1.0 起,subscribeAction 也可以指定订阅处理函数的被调用时机应该在一个 action 分发之前还是之后 (默认行为是之前)。
这是相应的代码部分,引用自官方文档。大家可以自行调试一下。
store.subscribeAction({
before: (action, state) => {
console.log(`before action ${action.type}`)
after: (action, state) => {
console.log(`after action ${action.type}`)
关于 genericSubscribe 方法,我在上一篇文章中已有介绍,这里不再重复。同时,关于在this._actionSubscribers上调用slice方法的原因,和上一篇文章谈到的对this._subscribers调用slice方法的原因是一样的(要仔细阅读一遍哟,一通百通嘛)。
一个store.dispatch在不同模块中可以触发多个action函数
还记得下面这段代码吗?这是dispatch源码中的另一重要知识点。
const result = entry.length > 1 ?
Promise.all(entry.map(handler => handler(payload))) : entry[0](payload)
在我们学习vuex关于Actions的教程时,你是否注意的最后一句话:
一个store.dispatch在不同模块中可以触发多个action函数。在这种情况下,只有当所有触发函数完成后,返回的Promise才会执行。
如你所想,这句话的对应实现,就是上面这段代码,其中关键就是Promise.all的使用。为更加清晰地理解上面的语句,我们举例说明一下。
位于vuex目录中的位置:examples/counter/store.js,在根模块和moduleB模块中注册一样的 action:increment,为了简便,仅贴出了需要改动的部分,其它部分都一样。
const actions = {
increment: ({ commit }) => commit('increment'),
const moduleB = {
actions: {
increment: ({ commit }) => commit('increment')
位于vuex目录中的位置:examples/counter/Counter.vue,直接在mounted生命周期函数中调用,方便测试。
mounted () {
this.$store.dispatch('increment')
位于vuex目录中的位置:src/store.js,找到dispatch源码部分打印参数。
store.dispatch().then()
大家到知道,调用store。dispatch分发action时,可以使用then,就是下面代码展示的那样。
store.dispatch('actionA').then(() => {
至于原因,若是大家已仔细阅读dispatch源码,那么便知道,它最后返回了一个Promise对象。
最近有点忙,在解读完commit源码后,一直想把dispatch源码也解读了,现在终于补上了。同时,若是小伙伴们看到了这篇文章并发现了有解读错误的地方,还请不吝赐教,大家一起共同进步加掉发。