diff --git a/src/store.js b/src/store.js index 65ca98567..c326b1943 100644 --- a/src/store.js +++ b/src/store.js @@ -35,6 +35,7 @@ export class Store { // store internal state this._committing = false this._actions = Object.create(null) + this._actionSubscribers = [] this._mutations = Object.create(null) this._wrappedGetters = Object.create(null) this._modules = new ModuleCollection(options) @@ -123,6 +124,7 @@ export class Store { payload } = unifyObjectStyle(_type, _payload) + const action = { type, payload } const entry = this._actions[type] if (!entry) { if (process.env.NODE_ENV !== 'production') { @@ -130,22 +132,20 @@ export class Store { } return } + + this._actionSubscribers.forEach(sub => sub(action, this.state)) + return entry.length > 1 ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) } subscribe (fn) { - const subs = this._subscribers - if (subs.indexOf(fn) < 0) { - subs.push(fn) - } - return () => { - const i = subs.indexOf(fn) - if (i > -1) { - subs.splice(i, 1) - } - } + return genericSubscribe(fn, this._subscribers) + } + + subscribeAction (fn) { + return genericSubscribe(fn, this._actionSubscribers) } watch (getter, cb, options) { @@ -203,6 +203,18 @@ export class Store { } } +function genericSubscribe (fn, subs) { + if (subs.indexOf(fn) < 0) { + subs.push(fn) + } + return () => { + const i = subs.indexOf(fn) + if (i > -1) { + subs.splice(i, 1) + } + } +} + function resetStore (store, hot) { store._actions = Object.create(null) store._mutations = Object.create(null) diff --git a/test/unit/modules.spec.js b/test/unit/modules.spec.js index 6ede57405..bc8fa2247 100644 --- a/test/unit/modules.spec.js +++ b/test/unit/modules.spec.js @@ -630,7 +630,9 @@ describe('Modules', () => { it('plugins', function () { let initState + const actionSpy = jasmine.createSpy() const mutations = [] + const subscribeActionSpy = jasmine.createSpy() const store = new Vuex.Store({ state: { a: 1 @@ -640,21 +642,31 @@ describe('Modules', () => { state.a += n } }, + actions: { + [TEST]: actionSpy + }, plugins: [ store => { initState = store.state store.subscribe((mut, state) => { - expect(state).toBe(store.state) + expect(state).toBe(state) mutations.push(mut) }) + store.subscribeAction(subscribeActionSpy) } ] }) expect(initState).toBe(store.state) store.commit(TEST, 2) + store.dispatch(TEST, 2) expect(mutations.length).toBe(1) expect(mutations[0].type).toBe(TEST) expect(mutations[0].payload).toBe(2) + expect(actionSpy).toHaveBeenCalled() + expect(subscribeActionSpy).toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state + ) }) }) diff --git a/test/unit/store.spec.js b/test/unit/store.spec.js index fdfa04282..3b586d7d2 100644 --- a/test/unit/store.spec.js +++ b/test/unit/store.spec.js @@ -286,6 +286,32 @@ describe('Store', () => { expect(store.state.a).toBe(3) }) + it('subscribe: should handle subscriptions / unsubscriptions', () => { + const subscribeSpy = jasmine.createSpy() + const secondSubscribeSpy = jasmine.createSpy() + const testPayload = 2 + const store = new Vuex.Store({ + state: {}, + mutations: { + [TEST]: () => {} + } + }) + + const unsubscribe = store.subscribe(subscribeSpy) + store.subscribe(secondSubscribeSpy) + store.commit(TEST, testPayload) + unsubscribe() + store.commit(TEST, testPayload) + + expect(subscribeSpy).toHaveBeenCalledWith( + { type: TEST, payload: testPayload }, + store.state + ) + expect(secondSubscribeSpy).toHaveBeenCalled() + expect(subscribeSpy.calls.count()).toBe(1) + expect(secondSubscribeSpy.calls.count()).toBe(2) + }) + // store.watch should only be asserted in non-SSR environment if (!isSSR) { it('strict mode: warn mutations outside of handlers', () => {