Module基础 #
为什么需要 Module? #
随着应用复杂度增加,store 对象会变得臃肿。Vuex 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。
定义模块 #
基本结构 #
javascript
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
// 访问模块状态
store.state.a // moduleA 的状态
store.state.b // moduleB 的状态
完整示例 #
javascript
// 模块定义
const userModule = {
state: () => ({
profile: null,
token: null
}),
mutations: {
SET_PROFILE(state, profile) {
state.profile = profile
},
SET_TOKEN(state, token) {
state.token = token
}
},
actions: {
async fetchProfile({ commit }) {
const profile = await api.fetchProfile()
commit('SET_PROFILE', profile)
}
},
getters: {
isLoggedIn: state => !!state.token,
userName: state => state.profile?.name || 'Guest'
}
}
const cartModule = {
state: () => ({
items: []
}),
mutations: {
ADD_ITEM(state, item) {
state.items.push(item)
},
CLEAR_CART(state) {
state.items = []
}
},
getters: {
itemCount: state => state.items.length,
total: state => state.items.reduce((sum, item) => sum + item.price, 0)
}
}
// 组合模块
const store = createStore({
modules: {
user: userModule,
cart: cartModule
}
})
模块的局部状态 #
State #
模块内的 state 是局部的,只能通过 store.state.moduleName 访问:
javascript
// 访问 user 模块的状态
this.$store.state.user.profile
this.$store.state.user.token
// 访问 cart 模块的状态
this.$store.state.cart.items
Mutation 和 Action #
模块内的 mutation 和 action 注册在全局命名空间:
javascript
// 调用 mutation
this.$store.commit('SET_PROFILE', profile) // 全局调用
// 分发 action
this.$store.dispatch('fetchProfile') // 全局调用
Getter #
模块内的 getter 也注册在全局:
javascript
// 访问 getter
this.$store.getters.isLoggedIn
this.$store.getters.itemCount
模块文件组织 #
推荐结构 #
text
store/
├── index.js # 入口文件,组装模块
├── mutation-types.js # Mutation 常量
└── modules/
├── user.js # 用户模块
├── cart.js # 购物车模块
└── products.js # 产品模块
模块文件示例 #
javascript
// store/modules/user.js
import * as types from '../mutation-types'
const state = () => ({
profile: null,
token: null,
loading: false,
error: null
})
const mutations = {
[types.SET_PROFILE](state, profile) {
state.profile = profile
},
[types.SET_TOKEN](state, token) {
state.token = token
},
[types.SET_LOADING](state, loading) {
state.loading = loading
},
[types.SET_ERROR](state, error) {
state.error = error
}
}
const actions = {
async login({ commit }, credentials) {
commit(types.SET_LOADING, true)
try {
const { user, token } = await api.login(credentials)
commit(types.SET_PROFILE, user)
commit(types.SET_TOKEN, token)
return user
} catch (error) {
commit(types.SET_ERROR, error.message)
throw error
} finally {
commit(types.SET_LOADING, false)
}
},
logout({ commit }) {
commit(types.SET_PROFILE, null)
commit(types.SET_TOKEN, null)
}
}
const getters = {
isLoggedIn: state => !!state.token,
userName: state => state.profile?.name || 'Guest',
userEmail: state => state.profile?.email || ''
}
export default {
namespaced: true, // 推荐开启命名空间
state,
mutations,
actions,
getters
}
入口文件 #
javascript
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import cart from './modules/cart'
import products from './modules/products'
export default createStore({
modules: {
user,
cart,
products
},
// 全局状态(可选)
state: {
appLoading: false,
appError: null
}
})
嵌套模块 #
定义嵌套模块 #
javascript
const store = createStore({
modules: {
account: {
namespaced: true,
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... },
modules: {
// 嵌套模块
posts: {
namespaced: true,
state: () => ({ ... }),
mutations: { ... }
},
comments: {
namespaced: true,
state: () => ({ ... }),
mutations: { ... }
}
}
}
}
})
// 访问嵌套模块状态
store.state.account.posts
store.state.account.comments
模块重用 #
创建可重用模块 #
javascript
function createCounterModule(initialValue = 0) {
return {
namespaced: true,
state: () => ({
count: initialValue
}),
mutations: {
INCREMENT(state) {
state.count++
},
DECREMENT(state) {
state.count--
},
SET_COUNT(state, value) {
state.count = value
}
},
actions: {
increment({ commit }) {
commit('INCREMENT')
},
decrement({ commit }) {
commit('DECREMENT')
}
},
getters: {
doubleCount: state => state.count * 2
}
}
}
// 使用可重用模块
const store = createStore({
modules: {
counterA: createCounterModule(10),
counterB: createCounterModule(20)
}
})
模块注册 #
静态注册 #
javascript
// 创建 store 时注册
const store = createStore({
modules: {
user: userModule,
cart: cartModule
}
})
动态注册 #
javascript
// 创建 store 后注册
store.registerModule('products', {
state: () => ({ items: [] }),
mutations: {
SET_ITEMS(state, items) {
state.items = items
}
}
})
// 访问动态注册的模块
store.state.products
// 卸载模块
store.unregisterModule('products')
最佳实践 #
1. 开启命名空间 #
javascript
export default {
namespaced: true, // 推荐
state,
mutations,
actions,
getters
}
2. 按功能划分模块 #
javascript
modules: {
user, // 用户相关
cart, // 购物车相关
products, // 产品相关
orders, // 订单相关
ui // UI 状态
}
3. 保持模块独立 #
javascript
// 推荐:模块独立,通过 action 通信
// user 模块
actions: {
async login({ dispatch }) {
// 登录成功后获取购物车
await dispatch('cart/fetchCart', null, { root: true })
}
}
// 不推荐:直接访问其他模块状态
actions: {
login({ rootState }) {
const cart = rootState.cart // 耦合度高
}
}
总结 #
Module 核心要点:
| 要点 | 说明 |
|---|---|
| 模块定义 | 独立的 state、mutations、actions、getters |
| 局部状态 | 模块 state 是局部的 |
| 全局注册 | 默认 mutations/actions/getters 是全局的 |
| 命名空间 | 推荐开启 namespaced |
| 文件组织 | 按功能模块拆分文件 |
继续学习 模块局部状态,深入了解模块内部状态管理。
最后更新:2026-03-28