Skip to content

Commit 61ae0be

Browse files
ktsnyyx990803
authored andcommitted
Allow nested modules (#220)
* Flatten the keys of given object on mergeObjects * Add utils for nested modules * Allow nested modules * Update test cases for nested modules * Allow nested modules have state and mutations * Allow nested modules only when it is defined under `modules` key
1 parent 2f1035a commit 61ae0be

File tree

3 files changed

+196
-44
lines changed

3 files changed

+196
-44
lines changed

src/index.js

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { mergeObjects, deepClone, getWatcher } from './util'
1+
import {
2+
mergeObjects, deepClone, isObject,
3+
getNestedState, getWatcher
4+
} from './util'
25
import devtoolMiddleware from './middlewares/devtool'
36
import override from './override'
47

@@ -142,8 +145,16 @@ class Store {
142145
*/
143146

144147
_setupModuleState (state, modules) {
148+
if (!isObject(modules)) return
149+
145150
Object.keys(modules).forEach(key => {
146-
Vue.set(state, key, modules[key].state || {})
151+
const module = modules[key]
152+
153+
// set this module's state
154+
Vue.set(state, key, module.state || {})
155+
156+
// retrieve nested modules
157+
this._setupModuleState(state[key], module.modules)
147158
})
148159
}
149160

@@ -156,24 +167,52 @@ class Store {
156167

157168
_setupModuleMutations (updatedModules) {
158169
const modules = this._modules
159-
const allMutations = [this._rootMutations]
160170
Object.keys(updatedModules).forEach(key => {
161171
modules[key] = updatedModules[key]
162172
})
163-
Object.keys(modules).forEach(key => {
173+
const updatedMutations = this._createModuleMutations(modules, [])
174+
this._mutations = mergeObjects([this._rootMutations, ...updatedMutations])
175+
}
176+
177+
/**
178+
* Helper method for _setupModuleMutations.
179+
* The method retrieve nested sub modules and
180+
* bind each mutations to its sub tree recursively.
181+
*
182+
* @param {Object} modules
183+
* @param {Array<String>} nestedKeys
184+
* @return {Array<Object>}
185+
*/
186+
187+
_createModuleMutations (modules, nestedKeys) {
188+
if (!isObject(modules)) return []
189+
190+
return Object.keys(modules).map(key => {
164191
const module = modules[key]
165-
if (!module || !module.mutations) return
192+
const newNestedKeys = nestedKeys.concat(key)
193+
194+
// retrieve nested modules
195+
const nestedMutations = this._createModuleMutations(module.modules, newNestedKeys)
196+
197+
if (!module || !module.mutations) {
198+
return mergeObjects(nestedMutations)
199+
}
200+
166201
// bind mutations to sub state tree
167202
const mutations = {}
168203
Object.keys(module.mutations).forEach(name => {
169204
const original = module.mutations[name]
170205
mutations[name] = (state, ...args) => {
171-
original(state[key], ...args)
206+
original(getNestedState(state, newNestedKeys), ...args)
172207
}
173208
})
174-
allMutations.push(mutations)
209+
210+
// merge mutations of this module and nested modules
211+
return mergeObjects([
212+
mutations,
213+
...nestedMutations
214+
])
175215
})
176-
this._mutations = mergeObjects(allMutations)
177216
}
178217

179218
/**

src/util.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ export function mergeObjects (arr) {
1313
// allow multiple mutation objects to contain duplicate
1414
// handlers for the same mutation type
1515
if (Array.isArray(existing)) {
16-
existing.push(obj[key])
16+
prev[key] = existing.concat(obj[key])
1717
} else {
18-
prev[key] = [prev[key], obj[key]]
18+
prev[key] = [existing].concat(obj[key])
1919
}
2020
} else {
2121
prev[key] = obj[key]
@@ -48,6 +48,28 @@ export function deepClone (obj) {
4848
}
4949
}
5050

51+
/**
52+
* Check whether the given value is Object or not
53+
*
54+
* @param {*} obj
55+
* @return {Boolean}
56+
*/
57+
58+
export function isObject (obj) {
59+
return obj !== null && typeof obj === 'object'
60+
}
61+
62+
/**
63+
* Get state sub tree by given keys.
64+
*
65+
* @param {Object} state
66+
* @param {Array<String>} nestedKeys
67+
* @return {Object}
68+
*/
69+
export function getNestedState (state, nestedKeys) {
70+
return nestedKeys.reduce((state, key) => state[key], state)
71+
}
72+
5173
/**
5274
* Hacks to get access to Vue internals.
5375
* Maybe we should expose these...

test/unit/test.js

Lines changed: 125 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,41 @@ describe('Vuex', () => {
6565
},
6666
mutations,
6767
modules: {
68-
one: {
68+
nested: {
6969
state: { a: 2 },
70-
mutations
70+
mutations,
71+
modules: {
72+
one: {
73+
state: { a: 3 },
74+
mutations
75+
},
76+
nested: {
77+
modules: {
78+
two: {
79+
state: { a: 4 },
80+
mutations
81+
},
82+
three: {
83+
state: { a: 5 },
84+
mutations
85+
}
86+
}
87+
}
88+
}
7189
},
72-
two: {
73-
state: { a: 3 },
90+
four: {
91+
state: { a: 6 },
7492
mutations
7593
}
7694
}
7795
})
7896
store.dispatch(TEST, 1)
7997
expect(store.state.a).to.equal(2)
80-
expect(store.state.one.a).to.equal(3)
81-
expect(store.state.two.a).to.equal(4)
98+
expect(store.state.nested.a).to.equal(3)
99+
expect(store.state.nested.one.a).to.equal(4)
100+
expect(store.state.nested.nested.two.a).to.equal(5)
101+
expect(store.state.nested.nested.three.a).to.equal(6)
102+
expect(store.state.four.a).to.equal(7)
82103
})
83104

84105
it('hot reload', function () {
@@ -93,20 +114,41 @@ describe('Vuex', () => {
93114
},
94115
mutations,
95116
modules: {
96-
one: {
117+
nested: {
97118
state: { a: 2 },
98-
mutations
119+
mutations,
120+
modules: {
121+
one: {
122+
state: { a: 3 },
123+
mutations
124+
},
125+
nested: {
126+
modules: {
127+
two: {
128+
state: { a: 4 },
129+
mutations
130+
},
131+
three: {
132+
state: { a: 5 },
133+
mutations
134+
}
135+
}
136+
}
137+
}
99138
},
100-
two: {
101-
state: { a: 3 },
139+
four: {
140+
state: { a: 6 },
102141
mutations
103142
}
104143
}
105144
})
106145
store.dispatch(TEST, 1)
107146
expect(store.state.a).to.equal(2)
108-
expect(store.state.one.a).to.equal(3)
109-
expect(store.state.two.a).to.equal(4)
147+
expect(store.state.nested.a).to.equal(3)
148+
expect(store.state.nested.one.a).to.equal(4)
149+
expect(store.state.nested.nested.two.a).to.equal(5)
150+
expect(store.state.nested.nested.three.a).to.equal(6)
151+
expect(store.state.four.a).to.equal(7)
110152

111153
// hot reload only root mutations
112154
store.hotUpdate({
@@ -118,34 +160,50 @@ describe('Vuex', () => {
118160
})
119161
store.dispatch(TEST, 1)
120162
expect(store.state.a).to.equal(1) // only root mutation updated
121-
expect(store.state.one.a).to.equal(4)
122-
expect(store.state.two.a).to.equal(5)
163+
expect(store.state.nested.a).to.equal(4)
164+
expect(store.state.nested.one.a).to.equal(5)
165+
expect(store.state.nested.nested.two.a).to.equal(6)
166+
expect(store.state.nested.nested.three.a).to.equal(7)
167+
expect(store.state.four.a).to.equal(8)
123168

124169
// hot reload modules
125170
store.hotUpdate({
126171
modules: {
127-
one: {
172+
nested: {
128173
state: { a: 234 },
129-
mutations: {
130-
[TEST] (state, n) {
131-
state.a += n
174+
mutations,
175+
modules: {
176+
one: {
177+
state: { a: 345 },
178+
mutations
179+
},
180+
nested: {
181+
modules: {
182+
two: {
183+
state: { a: 456 },
184+
mutations
185+
},
186+
three: {
187+
state: { a: 567 },
188+
mutations
189+
}
190+
}
132191
}
133192
}
134193
},
135-
two: {
136-
state: { a: 345 },
137-
mutations: {
138-
[TEST] (state, n) {
139-
state.a -= n
140-
}
141-
}
194+
four: {
195+
state: { a: 678 },
196+
mutations
142197
}
143198
}
144199
})
145200
store.dispatch(TEST, 2)
146201
expect(store.state.a).to.equal(2)
147-
expect(store.state.one.a).to.equal(6) // should not reload initial state
148-
expect(store.state.two.a).to.equal(3) // should not reload initial state
202+
expect(store.state.nested.a).to.equal(6) // should not reload initial state
203+
expect(store.state.nested.one.a).to.equal(7) // should not reload initial state
204+
expect(store.state.nested.nested.two.a).to.equal(8) // should not reload initial state
205+
expect(store.state.nested.nested.three.a).to.equal(9) // should not reload initial state
206+
expect(store.state.four.a).to.equal(10) // should not reload initial state
149207

150208
// hot reload all
151209
store.hotUpdate({
@@ -155,28 +213,61 @@ describe('Vuex', () => {
155213
}
156214
},
157215
modules: {
158-
one: {
216+
nested: {
159217
state: { a: 234 },
160218
mutations: {
161219
[TEST] (state, n) {
162-
state.a = n
220+
state.a += n
221+
}
222+
},
223+
modules: {
224+
one: {
225+
state: { a: 345 },
226+
mutations: {
227+
[TEST] (state, n) {
228+
state.a += n
229+
}
230+
}
231+
},
232+
nested: {
233+
modules: {
234+
two: {
235+
state: { a: 456 },
236+
mutations: {
237+
[TEST] (state, n) {
238+
state.a += n
239+
}
240+
}
241+
},
242+
three: {
243+
state: { a: 567 },
244+
mutations: {
245+
[TEST] (state, n) {
246+
state.a -= n
247+
}
248+
}
249+
}
250+
}
163251
}
164252
}
165253
},
166-
two: {
167-
state: { a: 345 },
254+
four: {
255+
state: { a: 678 },
168256
mutations: {
169257
[TEST] (state, n) {
170-
state.a = n
258+
state.a -= n
171259
}
172260
}
173261
}
174262
}
175263
})
176264
store.dispatch(TEST, 3)
177265
expect(store.state.a).to.equal(-1)
178-
expect(store.state.one.a).to.equal(3)
179-
expect(store.state.two.a).to.equal(3)
266+
expect(store.state.nested.a).to.equal(9)
267+
expect(store.state.nested.one.a).to.equal(10)
268+
expect(store.state.nested.nested.two.a).to.equal(11)
269+
expect(store.state.nested.nested.three.a).to.equal(6)
270+
expect(store.state.four.a).to.equal(7)
180271
})
181272

182273
it('middleware', function () {

0 commit comments

Comments
 (0)