0
# Module System
1
2
Modular organization system for complex applications with namespacing, dynamic registration, and hierarchical state management.
3
4
## Capabilities
5
6
### Module Definition
7
8
Define store modules with their own state, mutations, actions, and getters.
9
10
```typescript { .api }
11
/**
12
* Module definition interface
13
*/
14
interface Module<S, R> {
15
/** Enable/disable namespacing for this module */
16
namespaced?: boolean;
17
/** Module state object or function returning state */
18
state?: S | (() => S);
19
/** Module getters for derived state */
20
getters?: GetterTree<S, R>;
21
/** Module actions for async operations */
22
actions?: ActionTree<S, R>;
23
/** Module mutations for state changes */
24
mutations?: MutationTree<S>;
25
/** Sub-modules for nested organization */
26
modules?: ModuleTree<R>;
27
}
28
29
interface GetterTree<S, R> {
30
[key: string]: Getter<S, R>;
31
}
32
33
interface ActionTree<S, R> {
34
[key: string]: ActionHandler<S, R>;
35
}
36
37
interface MutationTree<S> {
38
[key: string]: Mutation<S>;
39
}
40
41
interface ModuleTree<R> {
42
[key: string]: Module<any, R>;
43
}
44
45
type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
46
type ActionHandler<S, R> = (context: ActionContext<S, R>, payload?: any) => any;
47
type Mutation<S> = (state: S, payload?: any) => any;
48
```
49
50
**Usage Examples:**
51
52
```typescript
53
// User module
54
const userModule = {
55
namespaced: true,
56
57
state: () => ({
58
profile: null,
59
preferences: {},
60
isAuthenticated: false
61
}),
62
63
mutations: {
64
setProfile(state, profile) {
65
state.profile = profile;
66
},
67
setAuthenticated(state, status) {
68
state.isAuthenticated = status;
69
}
70
},
71
72
actions: {
73
async login({ commit, dispatch }, credentials) {
74
const user = await api.login(credentials);
75
commit('setProfile', user);
76
commit('setAuthenticated', true);
77
78
// Call root action
79
dispatch('initializeApp', null, { root: true });
80
}
81
},
82
83
getters: {
84
displayName: state => state.profile?.name || 'Guest',
85
isLoggedIn: state => state.isAuthenticated && state.profile
86
}
87
};
88
89
// Cart module with sub-modules
90
const cartModule = {
91
namespaced: true,
92
93
state: () => ({
94
items: [],
95
discounts: []
96
}),
97
98
modules: {
99
shipping: shippingModule,
100
payment: paymentModule
101
},
102
103
mutations: {
104
addItem(state, item) {
105
const existing = state.items.find(i => i.id === item.id);
106
if (existing) {
107
existing.quantity += item.quantity;
108
} else {
109
state.items.push(item);
110
}
111
}
112
},
113
114
getters: {
115
total: state => state.items.reduce((sum, item) =>
116
sum + (item.price * item.quantity), 0),
117
itemCount: state => state.items.reduce((sum, item) =>
118
sum + item.quantity, 0)
119
}
120
};
121
```
122
123
### Dynamic Module Registration
124
125
Register modules dynamically at runtime.
126
127
```typescript { .api }
128
/**
129
* Register a module dynamically
130
* @param path - Module path (string or array)
131
* @param module - Module definition
132
* @param options - Registration options
133
*/
134
registerModule<T>(path: string | string[], module: Module<T, S>, options?: ModuleOptions): void;
135
136
interface ModuleOptions {
137
/** Preserve existing state when registering */
138
preserveState?: boolean;
139
}
140
```
141
142
**Usage Examples:**
143
144
```typescript
145
import { createStore } from 'vuex';
146
147
const store = createStore({
148
state: { count: 0 }
149
});
150
151
// Register single module
152
store.registerModule('user', userModule);
153
154
// Access: store.state.user.profile
155
// Commit: store.commit('user/setProfile', profile)
156
157
// Register nested module
158
store.registerModule(['cart', 'shipping'], shippingModule);
159
160
// Access: store.state.cart.shipping.address
161
// Commit: store.commit('cart/shipping/setAddress', address)
162
163
// Register with preserved state
164
store.registerModule('settings', settingsModule, {
165
preserveState: true // Don't replace existing settings state
166
});
167
168
// Register multiple modules
169
const modules = {
170
user: userModule,
171
cart: cartModule,
172
notifications: notificationModule
173
};
174
175
Object.keys(modules).forEach(name => {
176
store.registerModule(name, modules[name]);
177
});
178
```
179
180
### Dynamic Module Unregistration
181
182
Unregister modules dynamically to clean up resources.
183
184
```typescript { .api }
185
/**
186
* Unregister a module dynamically
187
* @param path - Module path (string or array)
188
*/
189
unregisterModule(path: string | string[]): void;
190
```
191
192
**Usage Examples:**
193
194
```typescript
195
// Unregister single module
196
store.unregisterModule('user');
197
198
// Unregister nested module
199
store.unregisterModule(['cart', 'shipping']);
200
201
// Conditional unregistration
202
if (store.hasModule('temporaryData')) {
203
store.unregisterModule('temporaryData');
204
}
205
206
// Clean up route-specific modules
207
router.beforeEach((to, from, next) => {
208
// Unregister previous route's modules
209
if (from.meta.storeModules) {
210
from.meta.storeModules.forEach(moduleName => {
211
if (store.hasModule(moduleName)) {
212
store.unregisterModule(moduleName);
213
}
214
});
215
}
216
217
// Register new route's modules
218
if (to.meta.storeModules) {
219
to.meta.storeModules.forEach(({ name, module }) => {
220
store.registerModule(name, module);
221
});
222
}
223
224
next();
225
});
226
```
227
228
### Module Existence Check
229
230
Check if a module is registered.
231
232
```typescript { .api }
233
/**
234
* Check if a module exists
235
* @param path - Module path (string or array)
236
* @returns True if module exists
237
*/
238
hasModule(path: string | string[]): boolean;
239
```
240
241
**Usage Examples:**
242
243
```typescript
244
// Check single module
245
if (store.hasModule('user')) {
246
console.log('User module is registered');
247
}
248
249
// Check nested module
250
if (store.hasModule(['cart', 'payment'])) {
251
store.dispatch('cart/payment/processPayment');
252
}
253
254
// Conditional registration
255
if (!store.hasModule('analytics')) {
256
store.registerModule('analytics', analyticsModule);
257
}
258
259
// Guard against missing modules
260
const safeDispatch = (action, payload) => {
261
const [module] = action.split('/');
262
if (store.hasModule(module)) {
263
return store.dispatch(action, payload);
264
} else {
265
console.warn(`Module ${module} not found for action ${action}`);
266
return Promise.resolve();
267
}
268
};
269
```
270
271
### Namespaced Module Access
272
273
Access namespaced module state, getters, mutations, and actions.
274
275
**Usage Examples:**
276
277
```typescript
278
const store = createStore({
279
modules: {
280
user: {
281
namespaced: true,
282
state: { profile: null },
283
mutations: { setProfile(state, profile) { state.profile = profile; } },
284
actions: {
285
async fetchProfile({ commit }, userId) {
286
const profile = await api.getProfile(userId);
287
commit('setProfile', profile);
288
}
289
},
290
getters: {
291
displayName: state => state.profile?.name || 'Guest'
292
}
293
},
294
295
cart: {
296
namespaced: true,
297
state: { items: [] },
298
modules: {
299
shipping: {
300
namespaced: true,
301
state: { address: null },
302
mutations: { setAddress(state, address) { state.address = address; } }
303
}
304
}
305
}
306
}
307
});
308
309
// Access namespaced state
310
const userProfile = store.state.user.profile;
311
const cartItems = store.state.cart.items;
312
const shippingAddress = store.state.cart.shipping.address;
313
314
// Access namespaced getters
315
const displayName = store.getters['user/displayName'];
316
317
// Commit namespaced mutations
318
store.commit('user/setProfile', profile);
319
store.commit('cart/shipping/setAddress', address);
320
321
// Dispatch namespaced actions
322
await store.dispatch('user/fetchProfile', userId);
323
324
// Using helpers with namespaces
325
import { mapState, mapActions } from 'vuex';
326
327
export default {
328
computed: {
329
...mapState('user', ['profile']),
330
...mapState('cart', {
331
cartItems: 'items'
332
})
333
},
334
methods: {
335
...mapActions('user', ['fetchProfile']),
336
...mapActions('cart', ['addItem'])
337
}
338
};
339
```
340
341
### Root Access from Modules
342
343
Access root state and dispatch root actions from within modules.
344
345
**Usage Examples:**
346
347
```typescript
348
const userModule = {
349
namespaced: true,
350
351
state: () => ({ profile: null }),
352
353
actions: {
354
async login({ commit, dispatch, rootState, rootGetters }, credentials) {
355
// Access root state
356
const appVersion = rootState.appVersion;
357
358
// Access root getters
359
const isOnline = rootGetters.isOnline;
360
361
if (!isOnline) {
362
throw new Error('Cannot login while offline');
363
}
364
365
const user = await api.login(credentials, { appVersion });
366
commit('setProfile', user);
367
368
// Dispatch root action
369
await dispatch('analytics/trackEvent', {
370
event: 'user_login',
371
userId: user.id
372
}, { root: true });
373
374
// Commit root mutation
375
commit('setLastLoginTime', Date.now(), { root: true });
376
}
377
},
378
379
getters: {
380
// Access root state in getters
381
canAccessPremium: (state, getters, rootState, rootGetters) => {
382
return state.profile?.tier === 'premium' || rootGetters.hasGlobalAccess;
383
}
384
}
385
};
386
```
387
388
### Module Hot Reloading
389
390
Support for hot module replacement during development.
391
392
**Usage Examples:**
393
394
```typescript
395
import { createStore } from 'vuex';
396
import userModule from './modules/user';
397
import cartModule from './modules/cart';
398
399
const store = createStore({
400
modules: {
401
user: userModule,
402
cart: cartModule
403
}
404
});
405
406
// Hot module replacement
407
if (module.hot) {
408
// Accept updates for modules
409
module.hot.accept(['./modules/user'], () => {
410
const newUserModule = require('./modules/user').default;
411
store.hotUpdate({
412
modules: {
413
user: newUserModule
414
}
415
});
416
});
417
418
// Accept updates for multiple modules
419
module.hot.accept([
420
'./modules/user',
421
'./modules/cart'
422
], () => {
423
const newUserModule = require('./modules/user').default;
424
const newCartModule = require('./modules/cart').default;
425
426
store.hotUpdate({
427
modules: {
428
user: newUserModule,
429
cart: newCartModule
430
}
431
});
432
});
433
}
434
435
export default store;
436
```
437
438
### Module Composition Patterns
439
440
Advanced patterns for organizing and composing modules.
441
442
**Usage Examples:**
443
444
```typescript
445
// Base module factory
446
const createCrudModule = (resource) => ({
447
namespaced: true,
448
449
state: () => ({
450
items: [],
451
loading: false,
452
error: null
453
}),
454
455
mutations: {
456
setItems(state, items) { state.items = items; },
457
setLoading(state, loading) { state.loading = loading; },
458
setError(state, error) { state.error = error; }
459
},
460
461
actions: {
462
async fetchAll({ commit }) {
463
commit('setLoading', true);
464
try {
465
const items = await api.getAll(resource);
466
commit('setItems', items);
467
} catch (error) {
468
commit('setError', error);
469
} finally {
470
commit('setLoading', false);
471
}
472
}
473
},
474
475
getters: {
476
byId: state => id => state.items.find(item => item.id === id),
477
count: state => state.items.length
478
}
479
});
480
481
// Create specific modules
482
const usersModule = {
483
...createCrudModule('users'),
484
actions: {
485
...createCrudModule('users').actions,
486
async login({ dispatch }, credentials) {
487
// User-specific action
488
const user = await api.login(credentials);
489
dispatch('fetchAll'); // Inherited action
490
return user;
491
}
492
}
493
};
494
495
const postsModule = createCrudModule('posts');
496
497
// Plugin-based module composition
498
const withCaching = (module, cacheKey) => ({
499
...module,
500
actions: {
501
...module.actions,
502
async fetchAll({ commit, state }) {
503
const cached = localStorage.getItem(cacheKey);
504
if (cached) {
505
commit('setItems', JSON.parse(cached));
506
} else {
507
await module.actions.fetchAll({ commit, state });
508
localStorage.setItem(cacheKey, JSON.stringify(state.items));
509
}
510
}
511
}
512
});
513
514
const cachedUsersModule = withCaching(usersModule, 'cached_users');
515
```