8000 support un-registering dynamic modules · poorprogrammer/vuejs.org@875684d · GitHub
[go: up one dir, main page]

Skip to content

Commit 875684d

Browse files
committed
support un-registering dynamic modules
1 parent e874d06 commit 875684d

File tree

2 files changed

+93
-29
lines changed

2 files changed

+93
-29
lines changed

src/index.js

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Store {
2121
this._actions = Object.create(null)
2222
this._mutations = Object.create(null)
2323
this._wrappedGetters = Object.create(null)
24+
this._runtimeModules = Object.create(null)
2425
this._subscribers = []
2526
this._pendingActions = []
2627

@@ -40,11 +41,11 @@ class Store {
4041
// init root module.
4142
// this also recursively registers all sub-modules
4243
// and collects all module getters inside this._wrappedGetters
43-
initModule(this, state, [], options)
44+
installModule(this, state, [], options)
4445

4546
// initialize the store vm, which is responsible for the reactivity
4647
// (also registers _wrappedGetters as computed properties)
47-
initStoreVM(this, state, this._wrappedGetters)
48+
resetStoreVM(this, state)
4849

4950
// apply plugins
5051
plugins.concat(devtoolPlugin).forEach(plugin => plugin(this))
@@ -72,11 +73,11 @@ class Store {
7273
console.error(`[vuex] unknown mutation type: ${type}`)
7374
return
7475
}
75-
this._committing = true
76-
entry.forEach(function commitIterator (handler) {
77-
handler(payload)
76+
this._withCommit(() => {
77+
entry.forEach(function commitIterator (handler) {
78+
handler(payload)
79+
})
7880
})
79-
this._committing = false
8081
if (!payload || !payload.silent) {
8182
this._subscribers.forEach(sub => sub(mutation, this.state))
8283
}
@@ -118,24 +119,32 @@ class Store {
118119
}
119120

120121
replaceState (state) {
121-
this._committing = true
122-
this._vm.state = state
123-
this._committing = false
122+
this._withCommit(() => {
123+
this._vm.state = state
124+
})
124125
}
125126

126-
registerModule (path, module, hot) {
127-
this._committing = true
127+
registerModule (path, module) {
128128
if (typeof path === 'string') path = [path]
129129
assert(Array.isArray(path), `module path must be a string or an Array.`)
130-
initModule(this, this.state, path, module, hot)
131-
initStoreVM(this, this.state, this._wrappedGetters)
132-
this._committing = false
130+
this._runtimeModules[path.join('.')] = module
131+
installModule(this, this.state, path, module)
132+
// reset store to update getters...
133+
resetStoreVM(this, this.state)
134+
}
135+
136+
unregisterModule (path) {
137+
if (typeof path === 'string') path = [path]
138+
assert(Array.isArray(path), `module path must be a string or an Array.`)
139+
delete this._runtimeModules[path.join('.')]
140+
this._withCommit(() => {
141+
const parentState = getNestedState(this.state, path.slice(0, -1))
142+
Vue.delete(parentState, path[path.length - 1])
143+
})
144+
resetStore(this)
133145
}
134146

135147
hotUpdate (newOptions) {
136-
this._actions = Object.create(null)
137-
this._mutations = Object.create(null)
138-
this._wrappedGetters = Object.create(null)
139148
const options = this._options
140149
if (newOptions.actions) {
141150
options.actions = newOptions.actions
@@ -151,26 +160,49 @@ class Store {
151160
options.modules[key] = newOptions.modules[key]
152161
}
153162
}
154-
this.registerModule([], options, true)
163+
resetStore(this)
155164
}
156165

157166
onActionsResolved (cb) {
158167
Promise.all(this._pendingActions).then(cb)
159168
}
169+
170+
_withCommit (fn) {
171+
const committing = this._committing
172+
this._committing = true
173+
fn()
174+
this._committing = committing
175+
}
160176
}
161177

162178
function assert (condition, msg) {
163179
if (!condition) throw new Error(`[vuex] ${msg}`)
164180
}
165181

166-
function initStoreVM (store, state, getters) {
182+
function resetStore (store) {
183+
store._actions = Object.create(null)
184+
store._mutations = Object.create(null)
185+
store._wrappedGetters = Object.create(null)
186+
const state = store.state
187+
// init root module
188+
installModule(store, state, [], store._options, true)
189+
// init all runtime modules
190+
Object.keys(store._runtimeModules).forEach(key => {
191+
installModule(store, state, key.split('.'), store._runtimeModules[key], true)
192+
})
193+
// reset vm
194+
resetStoreVM(store, state)
195+
}
196+
197+
function resetStoreVM (store, state) {
167198
const oldVm = store._vm
168199

169-
// bind getters
200+
// bind store public getters
170201
store.getters = {}
202+
const wrappedGetters = store._wrappedGetters
171203
const computed = {}
172-
Object.keys(getters).forEach(key => {
173-
const fn = getters[key]
204+
Object.keys(wrappedGetters).forEach(key => {
205+
const fn = wrappedGetters[key]
174206
// use computed to leverage its lazy-caching mechanism
175207
computed[key] = () => fn(store)
176208
Object.defineProperty(store.getters, key, {
@@ -197,14 +229,14 @@ function initStoreVM (store, state, getters) {
197229
if (oldVm) {
198230
// dispatch changes in all subscribed watchers
199231
// to force getter re-evaluation.
200-
store._committing = true
201-
oldVm.state = null
202-
store._committing = false
232+
store._withCommit(() => {
233+
oldVm.state = null
234+
})
203235
Vue.nextTick(() => oldVm.$destroy())
204236
}
205237
}
206238

207-
function initModule (store, rootState, path, module, hot) {
239+
function installModule (store, rootState, path, module, hot) {
208240
const isRoot = !path.length
209241
const {
210242
state,
@@ -218,7 +250,9 @@ function initModule (store, rootState, path, module, hot) {
218250
if (!isRoot && !hot) {
219251
const parentState = getNestedState(rootState, path.slice(0, -1))
220252
const moduleName = path[path.length - 1]
221-
Vue.set(parentState, moduleName, state || {})
253+
store._withCommit(() => {
254+
Vue.set(parentState, moduleName, state || {})
255+
})
222256
}
223257

224258
if (mutations) {
@@ -239,7 +273,7 @@ function initModule (store, rootState, path, module, hot) {
239273

240274
if (modules) {
241275
Object.keys(modules).forEach(key => {
242-
initModule(store, rootState, path.concat(key), modules[key], hot)
276+
installModule(store, rootState, path.concat(key), modules[key], hot)
243277
})
244278
}
245279
}

test/unit/test.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,15 @@ describe('Vuex', () => {
199199

200200
it('dynamic module registration', () => {
201201
const store = new Vuex.Store({
202-
strict: true
202+
strict: true,
203+
modules: {
204+
foo: {
205+
state: { bar: 1 },
206+
mutations: { inc: state => state.bar++ } F42D ,
207+
actions: { incFoo: ({ commit }) => commit('inc') },
208+
getters: { bar: state => state.bar }
209+
}
210+
}
203211
})
204212
expect(() => {
205213
store.registerModule('hi', {
@@ -209,11 +217,33 @@ describe('Vuex', () => {
209217
getters: { a: state => state.a }
210218
})
211219
}).not.toThrow()
220+
221+
expect(store._mutations.inc.length).toBe(2)
212222
expect(store.state.hi.a).toBe(1)
213223
expect(store.getters.a).toBe(1)
224+
225+
// assert initial modules work as expected after dynamic registration
226+
expect(store.state.foo.bar).toBe(1)
227+
expect(store.getters.bar).toBe(1)
228+
229+
// test dispatching actions defined in dynamic module
214230
store.dispatch('inc')
215231
expect(store.state.hi.a).toBe(2)
216232
expect(store.getters.a).toBe(2)
233+
expect(store.state.foo.bar).toBe(2)
234+
expect(store.getters.bar).toBe(2)
235+
236+
// unregister
237+
store.unregisterModule('hi')
238+
expect(store.state.hi).toBeUndefined()
239+
expect(store.getters.a).toBeUndefined()
240+
expect(store._mutations.inc.length).toBe(1)
241+
expect(store._actions.inc).toBeUndefined()
242+
243+
// assert initial modules still work as expected after unregister
244+
store.dispatch('incFoo')
245+
expect(store.state.foo.bar).toBe(3)
246+
expect(store.getters.bar).toBe(3)
217247
})
218248

219249
it('store injection', () => {

0 commit comments

Comments
 (0)
0