Appearance
dispatch
javascript
class Store {
constructor () {
// store.prototype.dispatch 接收 type 和 payload 两个个参数
// 根据 type 去 store._actions 中找出对应的数组,这个数组内存储的都是这个 type
// 的 action,遍历数组并执行 wrappedActionHandler 同时传入 payload。
// 直接这样做有个问题。
// context.commit 或 context.dispatch 会调用
// store.prototype.commit 或 store.prototype.dispatch,此时 this 是 context
// context._mutations 或 context._actions 值为 undefined。
// this._actions[type] 或 this._mutations[type] 就会报错
// 或者我们对 action 的 context 解构出 dispatch 和 commit
// 再调用 dispatch 或 commit 时,会调用
// store.prototype.commit 或 tore.prototype.dispatch,此时 this 是 undefined
// this._actions[type] 或 this._mutations[type] 就会报错
// 因此不能让实例直接调用父类的 dispatch 和 commit 方法
// 而是需要调用前需要改变父类的 dispatch 和 commit 方法的 this,因此需要劫持。
// 在实例自身中也定义 dispatch 和 commit 方法,这样最先执行的是实例自身的方法
// 实例自身的方法中再执行原型中的 dispatch 和 commit 并修改this 为 store
// 同时传入 type 和 payload,但是实例的方法已经重写原型的,因此在定义实例
// 方法前先取出原型中的方法并保存即可。
// 以上操作必须在 installModule 之前完成,因为 installModule 中生成 context,
// context.commit = store.commit,如果在之后完成,context.commit 总是执行
// Store.prototype.commit,不会执行我们定义在 store 上的,就没机会修改 this 了。
// bind commit and dispatch to self
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
return commit.call(store, type, payload, options)
}
}
commit (type, payload) {
const entry = this._mutations[type]
if (!entry) {
return
}
entry.forEach(function commitIterator (handler) {
handler(payload)
})
}
dispatch (type, payload) {
const entry = this._actions[type]
if (!entry) {
return
}
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)
})
})
}
}