From 5027b735950c8cb3ae65de6ee9ab1aced743d1ef Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 16 Dec 2016 16:51:10 -0500 Subject: [PATCH] support namespace in helpers --- src/helpers.js | 50 +++++++++++++---- src/index.js | 14 +++-- test/unit/helpers.spec.js | 114 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 15 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index 54c729820..70b299066 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,28 +1,41 @@ -export function mapState (states) { +export const mapState = normalizeNamespace((namespace, states) => { const res = {} normalizeMap(states).forEach(({ key, val }) => { res[key] = function mappedState () { + let state = this.$store.state + let getters = this.$store.getters + if (namespace) { + const module = this.$store._modulesNamespaceMap[namespace] + if (!module) { + warnNamespace('mapState', namespace) + return + } + state = module.state + getters = module.context.getters + } return typeof val === 'function' - ? val.call(this, this.$store.state, this.$store.getters) - : this.$store.state[val] + ? val.call(this, state, getters) + : state[val] } }) return res -} +}) -export function mapMutations (mutations) { +export const mapMutations = normalizeNamespace((namespace, mutations) => { const res = {} normalizeMap(mutations).forEach(({ key, val }) => { + val = namespace + val res[key] = function mappedMutation (...args) { return this.$store.commit.apply(this.$store, [val].concat(args)) } }) return res -} +}) -export function mapGetters (getters) { +export const mapGetters = normalizeNamespace((namespace, getters) => { const res = {} normalizeMap(getters).forEach(({ key, val }) => { + val = namespace + val res[key] = function mappedGetter () { if (!(val in this.$store.getters)) { console.error(`[vuex] unknown getter: ${val}`) @@ -31,20 +44,37 @@ export function mapGetters (getters) { } }) return res -} +}) -export function mapActions (actions) { +export const mapActions = normalizeNamespace((namespace, actions) => { const res = {} normalizeMap(actions).forEach(({ key, val }) => { + val = namespace + val res[key] = function mappedAction (...args) { return this.$store.dispatch.apply(this.$store, [val].concat(args)) } }) return res -} +}) function normalizeMap (map) { return Array.isArray(map) ? map.map(key => ({ key, val: key })) : Object.keys(map).map(key => ({ key, val: map[key] })) } + +function normalizeNamespace (fn) { + return (namespace, map) => { + if (typeof namespace !== 'string') { + map = namespace + namespace = '' + } else if (namespace.charAt(namespace.length - 1) !== '/') { + namespace += '/' + } + return fn(namespace, map) + } +} + +function warnNamespace (helper, namespace) { + console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`) +} diff --git a/src/index.js b/src/index.js index b01153333..b3633b08f 100644 --- a/src/index.js +++ b/src/index.js @@ -23,6 +23,7 @@ class Store { this._mutations = Object.create(null) this._wrappedGetters = Object.create(null) this._modules = new ModuleCollection(options) + this._modulesNamespaceMap = Object.create(null) this._subscribers = [] this._watcherVM = new Vue() @@ -81,10 +82,7 @@ class Store { }) this._subscribers.forEach(sub => sub(mutation, this.state)) - if ( - process.env.NODE_ENV !== 'production' && - options && options.hasOwnProperty('silent') - ) { + if (options && options.silent) { console.warn( `[vuex] mutation type: ${type}. Silent option has been removed. ` + 'Use the filter functionality in the vue-devtools' @@ -170,6 +168,7 @@ function resetStore (store) { store._actions = Object.create(null) store._mutations = Object.create(null) store._wrappedGetters = Object.create(null) + store._modulesNamespaceMap = Object.create(null) const state = store.state // init all modules installModule(store, state, [], store._modules.root, true) @@ -223,6 +222,11 @@ function installModule (store, rootState, path, module, hot) { const isRoot = !path.length const namespace = store._modules.getNamespace(path) + // register in namespace map + if (namespace) { + store._modulesNamespaceMap[namespace] = module + } + // set state if (!isRoot && !hot) { const parentState = getNestedState(rootState, path.slice(0, -1)) @@ -232,7 +236,7 @@ function installModule (store, rootState, path, module, hot) { }) } - const local = makeLocalContext(store, namespace) + const local = module.context = makeLocalContext(store, namespace) module.forEachMutation((mutation, key) => { const namespacedType = namespace + key diff --git a/test/unit/helpers.spec.js b/test/unit/helpers.spec.js index b52bcc805..a883d7aa1 100644 --- a/test/unit/helpers.spec.js +++ b/test/unit/helpers.spec.js @@ -39,6 +39,31 @@ describe('Helpers', () => { expect(vm.a).toBe(4) }) + it('mapState (with namespace)', () => { + const store = new Vuex.Store({ + modules: { + foo: { + namespaced: true, + state: { a: 1 }, + getters: { + b: state => state.a + 1 + } + } + } + }) + const vm = new Vue({ + store, + computed: mapState('foo', { + a: (state, getters) => { + return state.a + getters.b + } + }) + }) + expect(vm.a).toBe(3) + store.state.foo.a++ + expect(vm.a).toBe(5) + }) + it('mapMutations (array)', () => { const store = new Vuex.Store({ state: { count: 0 }, @@ -78,6 +103,32 @@ describe('Helpers', () => { expect(store.state.count).toBe(0) }) + it('mapMutations (with namespace)', () => { + const store = new Vuex.Store({ + modules: { + foo: { + namespaced: true, + state: { count: 0 }, + mutations: { + inc: state => state.count++, + dec: state => state.count-- + } + } + } + }) + const vm = new Vue({ + store, + methods: mapMutations('foo', { + plus: 'inc', + minus: 'dec' + }) + }) + vm.plus() + expect(store.state.foo.count).toBe(1) + vm.minus() + expect(store.state.foo.count).toBe(0) + }) + it('mapGetters (array)', () => { const store = new Vuex.Store({ state: { count: 0 }, @@ -135,6 +186,41 @@ describe('Helpers', () => { expect(vm.b).toBe(true) }) + it('mapGetters (with namespace)', () => { + const store = new Vuex.Store({ + modules: { + foo: { + namespaced: true, + state: { count: 0 }, + mutations: { + inc: state => state.count++, + dec: state => state.count-- + }, + getters: { + hasAny: ({ count }) => count > 0, + negative: ({ count }) => count < 0 + } + } + } + }) + const vm = new Vue({ + store, + computed: mapGetters('foo', { + a: 'hasAny', + b: 'negative' + }) + }) + expect(vm.a).toBe(false) + expect(vm.b).toBe(false) + store.commit('foo/inc') + expect(vm.a).toBe(true) + expect(vm.b).toBe(false) + store.commit('foo/dec') + store.commit('foo/dec') + expect(vm.a).toBe(false) + expect(vm.b).toBe(true) + }) + it('mapActions (array)', () => { const a = jasmine.createSpy() const b = jasmine.createSpy() @@ -177,4 +263,32 @@ describe('Helpers', () => { vm.bar() expect(b).toHaveBeenCalled() }) + + it('mapActions (with namespace)', () => { + const a = jasmine.createSpy() + const b = jasmine.createSpy() + const store = new Vuex.Store({ + modules: { + foo: { + namespaced: true, + actions: { + a, + b + } + } + } + }) + const vm = new Vue({ + store, + methods: mapActions('foo/', { + foo: 'a', + bar: 'b' + }) + }) + vm.foo() + expect(a).toHaveBeenCalled() + expect(b).not.toHaveBeenCalled() + vm.bar() + expect(b).toHaveBeenCalled() + }) })