项目结构 #
推荐目录结构 #
标准结构 #
text
src/
├── store/
│ ├── index.js # 入口文件,组装模块并导出 store
│ ├── mutation-types.js # Mutation 常量
│ ├── actions.js # 根级别的 action(可选)
│ ├── mutations.js # 根级别的 mutation(可选)
│ ├── getters.js # 根级别的 getter(可选)
│ └── modules/ # 模块目录
│ ├── user.js # 用户模块
│ ├── cart.js # 购物车模块
│ ├── products.js # 产品模块
│ └── ui.js # UI 状态模块
├── composables/ # 组合式函数
│ ├── useUserStore.js
│ ├── useCartStore.js
│ └── useProductStore.js
└── main.js
大型项目结构 #
text
src/
├── store/
│ ├── index.js
│ ├── mutation-types.js
│ ├── modules/
│ │ ├── user/
│ │ │ ├── index.js # 模块入口
│ │ │ ├── state.js # 状态
│ │ │ ├── mutations.js # 变更
│ │ │ ├── actions.js # 动作
│ │ │ └── getters.js # 派生状态
│ │ ├── cart/
│ │ │ ├── index.js
│ │ │ ├── state.js
│ │ │ ├── mutations.js
│ │ │ ├── actions.js
│ │ │ └── getters.js
│ │ └── products/
│ │ └── ...
│ └── plugins/
│ ├── persistence.js # 持久化插件
│ └── logger.js # 日志插件
└── composables/
└── ...
入口文件 #
基本入口 #
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
},
strict: process.env.NODE_ENV !== 'production'
})
完整入口 #
javascript
// store/index.js
import { createStore } from 'vuex'
import * as types from './mutation-types'
// 模块
import user from './modules/user'
import cart from './modules/cart'
import products from './modules/products'
import ui from './modules/ui'
// 插件
import createPersistence from './plugins/persistence'
const debug = process.env.NODE_ENV !== 'production'
const store = createStore({
modules: {
user,
cart,
products,
ui
},
// 根状态
state: {
appVersion: '1.0.0',
appReady: false
},
// 根 mutation
mutations: {
[types.SET_APP_READY](state, ready) {
state.appReady = ready
}
},
// 根 action
actions: {
async initApp({ dispatch, commit }) {
await Promise.all([
dispatch('user/checkAuth'),
dispatch('products/fetchCategories')
])
commit(types.SET_APP_READY, true)
}
},
// 根 getter
getters: {
appVersion: state => state.appVersion,
isAppReady: state => state.appReady
},
// 插件
plugins: debug
? [createLogger()]
: [createPersistence()],
// 严格模式
strict: debug
})
export default store
模块文件 #
单文件模块 #
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_USER_PROFILE](state, profile) {
state.profile = profile
},
[types.SET_USER_TOKEN](state, token) {
state.token = token
},
[types.SET_USER_LOADING](state, loading) {
state.loading = loading
},
[types.SET_USER_ERROR](state, error) {
state.error = error
}
}
const actions = {
async login({ commit }, credentials) {
commit(types.SET_USER_LOADING, true)
commit(types.SET_USER_ERROR, null)
try {
const { user, token } = await api.login(credentials)
commit(types.SET_USER_PROFILE, user)
commit(types.SET_USER_TOKEN, token)
return user
} catch (error) {
commit(types.SET_USER_ERROR, error.message)
throw error
} finally {
commit(types.SET_USER_LOADING, false)
}
},
logout({ commit }) {
commit(types.SET_USER_PROFILE, null)
commit(types.SET_USER_TOKEN, null)
}
}
const getters = {
isLoggedIn: state => !!state.token,
userName: state => state.profile?.name || 'Guest'
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
多文件模块 #
javascript
// store/modules/user/index.js
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
javascript
// store/modules/user/state.js
export default () => ({
profile: null,
token: null,
loading: false,
error: null
})
javascript
// store/modules/user/mutations.js
import * as types from '../../mutation-types'
export default {
[types.SET_USER_PROFILE](state, profile) {
state.profile = profile
},
[types.SET_USER_TOKEN](state, token) {
state.token = token
}
}
Mutation 类型文件 #
javascript
// store/mutation-types.js
// User
export const SET_USER_PROFILE = 'SET_USER_PROFILE'
export const SET_USER_TOKEN = 'SET_USER_TOKEN'
export const SET_USER_LOADING = 'SET_USER_LOADING'
export const SET_USER_ERROR = 'SET_USER_ERROR'
// Cart
export const ADD_CART_ITEM = 'ADD_CART_ITEM'
export const REMOVE_CART_ITEM = 'REMOVE_CART_ITEM'
export const UPDATE_CART_QUANTITY = 'UPDATE_CART_QUANTITY'
export const CLEAR_CART = 'CLEAR_CART'
// Products
export const SET_PRODUCTS = 'SET_PRODUCTS'
export const SET_CATEGORIES = 'SET_CATEGORIES'
export const SET_CURRENT_PRODUCT = 'SET_CURRENT_PRODUCT'
// UI
export const SET_LOADING = 'SET_LOADING'
export const SET_ERROR = 'SET_ERROR'
export const SET_TOAST = 'SET_TOAST'
模块命名规范 #
按功能命名 #
text
modules/
├── auth.js # 认证
├── user.js # 用户
├── cart.js # 购物车
├── products.js # 产品
├── orders.js # 订单
├── notifications.js # 通知
└── ui.js # UI 状态
按领域命名 #
text
modules/
├── account/ # 账户领域
│ ├── profile.js
│ └── settings.js
├── commerce/ # 商业领域
│ ├── cart.js
│ ├── products.js
│ └── orders.js
└── system/ # 系统领域
├── notifications.js
└── ui.js
状态结构设计 #
扁平化设计 #
javascript
// 推荐:扁平化
state: {
users: {
byId: {
1: { id: 1, name: 'John' },
2: { id: 2, name: 'Jane' }
},
allIds: [1, 2]
}
}
// 不推荐:深层嵌套
state: {
data: {
users: {
list: {
items: []
}
}
}
}
分离业务数据和 UI 状态 #
javascript
// 业务数据
state: {
entities: {
users: {},
products: {},
orders: {}
}
}
// UI 状态
state: {
ui: {
loading: false,
error: null,
selectedId: null,
filters: {}
}
}
最佳实践 #
1. 模块独立 #
javascript
// 每个模块应该是独立的
export default {
namespaced: true, // 开启命名空间
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
2. 统一导出 #
javascript
// store/modules/index.js
export { default as user } from './user'
export { default as cart } from './cart'
export { default as products } from './products'
// store/index.js
import * as modules from './modules'
export default createStore({
modules
})
3. 类型安全 #
typescript
// 使用 TypeScript 提供类型支持
interface UserState {
profile: User | null
token: string | null
}
const state: () => UserState = () => ({
profile: null,
token: null
})
总结 #
项目结构要点:
| 要点 | 说明 |
|---|---|
| 模块化 | 按功能分割模块 |
| 命名空间 | 开启 namespaced |
| 常量命名 | 使用 mutation-types.js |
| 扁平化 | 避免深层嵌套 |
| 分离关注点 | 业务数据与 UI 状态分离 |
继续学习 Vuex与TypeScript,了解 TypeScript 集成。
最后更新:2026-03-28