dispatch 源码解析

3/5/2021 Vuex

Action 通过 store.dispatch 方法触发:

store.dispatch('request')
1

Action 的内部是不受约束的,即不管是异步还是同步都是可以支持的。 Actions 支持同样的载荷方式和对象方式进行分发:

// 以载荷形式分发
store.dispatch('incrementAsync', {
    amount: 10
})

// 以对象形式分发
store.dispatch({
    type: 'incrementAsync',
    amount: 10
})
1
2
3
4
5
6
7
8
9
10

来看一个更加实际的购物车示例,涉及到调用异步 API 和分发多重 mutation

actions: {
    checkout ({ commit, state }, products) {
        // 把当前购物车的物品备份起来
        const savedCartItems = [...state.cart.added]
        // 发出结账请求,然后乐观地清空购物车
        commit(types.CHECKOUT_REQUEST)
        // 购物 API 接受一个成功回调和一个失败回调
        shop.buyProducts(
            products,
            // 成功操作
            () => commit(types.CHECKOUT_SUCCESS),
            // 失败操作
            () => commit(types.CHECKOUT_FAILURE, savedCartItems)
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上面的 actions.checkout,第一个参数位是 context,第二个参数位是 payload。我们来看看代码是如何实现的。

Store.prototype.dispatch = function dispatch(_type, _payload) {
    var this$1 = this;

    // check object-style dispatch
    // Tip: 统一(unify)参数格式
    var ref = unifyObjectStyle(_type, _payload);
    var type = ref.type;
    var payload = ref.payload;

    var action = {
        type: type,
        payload: payload
    };
    // Tip: this._actions 里面存储的是啥? entry是一个Promise Array, 
    var entry = this._actions[type];
    if (!entry) {
        {
            console.error(("[vuex] unknown action type: " + type));
        }
        return
    }
    try {
        // Tip: 在 dispatch 使用 sub.before 加工 action
        // Tip: 在 dispatch action 之前,触发订阅 actions 的猴子函数,这里是 before
        // 如果订户同步调用退订,则浅表副本可防止迭代器无效
        this._actionSubscribers
            .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
            .filter(function (sub) {
                return sub.before;
            })
            .forEach(function (sub) {
                return sub.before(action, this$1.state);
            });
    } catch (e) {
        {
            console.warn("[vuex] error in before action subscribers: ");
            console.error(e);
        }
    }

    // 执行 entry 里面的函数,即被封装的 actions
    var result = entry.length > 1 ?
        // Todo: 一个 type 会对应多个 action 吗?
        Promise.all(entry.map(function (handler) {
            return handler(payload);
        })) :
        entry[0](payload);

    return new Promise(function (resolve, reject) {
        result.then(function (res) {
            try {
                // Tip:在 dispatch 使用 sub.after 加工 action
                this$1._actionSubscribers
                    .filter(function (sub) {
                        return sub.after;
                    })
                    .forEach(function (sub) {
                        return sub.after(action, this$1.state);
                    });
            } catch (e) {
                {
                    console.warn("[vuex] error in after action subscribers: ");
                    console.error(e);
                }
            }
            resolve(res);
        }, function (error) {
            try {
                this$1._actionSubscribers
                    .filter(function (sub) {
                        return sub.error;
                    })
                    .forEach(function (sub) {
                        return sub.error(action, this$1.state, error);
                    });
            } catch (e) {
                {
                    console.warn("[vuex] error in error action subscribers: ");
                    console.error(e);
                }
            }
            reject(error);
        });
    })
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

使用 unifyObjectStyle 统一接口所要支持的传参方式,即 以载荷形式分发以对象形式分发,生成标准化 action

依据 action.typethis._actions 获取到 wrappedActionHandler

拿到 action 执行结束的 ressult,这是个 Promise, Promise.then 中分别执行 _actionSubscribersaftererror钩子函数。

在这里还有一个疑问:action 的上下文是如何注入的?可以去了解一下初始化的过程。

上次更新: 2/18/2025, 2:29:21 PM