Vuex
Vue.js 的经典集中式状态管理库,曾作为 Vue 生态的核心状态管理方案,现已进入维护模式。
Vuex 是 Vue 生态最早的集中式状态管理库,采用单一数据源和严格单向数据流设计。2021 年 Pinia 被宣布为 Vue 3 默认方案后,Vuex 进入维护模式,不再添加新功能。在 Vue 2 存量项目和部分早期 Vue 3 项目中仍广泛使用。
Vuex
Vue.js 的经典集中式状态管理库,曾作为 Vue 生态的核心状态管理方案,现已进入维护模式。
技术简介说明
Vuex 是 Vue.js 生态中最早也是最经典的集中式状态管理库,由 Vue 作者尤雨溪(Evan You)于 2015 年创建,灵感来源于 Flux、Redux 和 Elm Architecture。Vuex 的核心思想是"单一数据源"(Single Source of Truth)——将应用中所有组件的共享状态提取到一个全局 Store 中,以集中、可预测的方式管理状态的读取和修改,从而解决大型 Vue 应用中组件间状态共享和同步的复杂性问题。
Vuex 的发展历程与 Vue 的版本演进紧密相连:Vuex 1/2 服务于 Vue 1.x/2.x,Vuex 3 是 Vue 2 的标配状态管理方案,Vuex 4 则适配 Vue 3 的响应式系统。在其鼎盛时期,Vuex 的 GitHub 仓库拥有超过 28.3k Stars,几乎每个中大型 Vue 项目都依赖 Vuex 管理全局状态,是 Vue 生态中不可或缺的基础设施。
然而,随着 Vue 3 和 Composition API 的到来,Vuex 的 API 设计(如 mutations、namespaced modules、辅助映射函数等)在类型推导和开发体验上暴露出诸多局限。2021 年,Vue 官方宣布 Pinia 为新的默认状态管理方案,Vuex 正式进入维护模式——不再增加新功能,仅修复关键 Bug。Vuex 5 的 RFC 讨论最终也因 Pinia 的成熟而关闭。尽管如此,Vuex 在 Vue 生态发展史上具有不可磨灭的历史地位,至今仍有大量存量项目在使用 Vuex 3(Vue 2)和 Vuex 4(Vue 3)。
基本信息
- 官网: https://vuex.vuejs.org
- GitHub: https://github.com/vuejs/vuex
- License: MIT
- 最新版本: v4.1.0(Vue 3)/ v3.6.2(Vue 2)
- 主要维护者/公司: Evan You(尤雨溪),Vue.js 官方团队(已进入维护模式,仅修复关键 Bug)
快速上手
安装(npm/yarn/pnpm)
# Vuex 4(Vue 3 项目)
npm install vuex@next
# 或指定版本
npm install [email protected]
# Vuex 3(Vue 2 项目)
npm install vuex@3
# yarn
yarn add vuex@next # Vue 3
yarn add vuex@3 # Vue 2
# pnpm
pnpm add vuex@next # Vue 3
pnpm add vuex@3 # Vue 2⚠️ 注意:Vuex 已进入维护模式,新项目请使用 Pinia。
基础配置
在 Vue 3 应用中安装 Vuex 4:
// main.ts
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from './App.vue'
// 创建 Store 实例
const store = createStore({
state() {
return {
count: 0,
}
},
mutations: {
increment(state: { count: number }) {
state.count++
},
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
},
},
getters: {
doubleCount: (state: { count: number }) => state.count * 2,
},
})
const app = createApp(App)
app.use(store)
app.mount('#app')最小示例
// store/index.ts
import { createStore } from 'vuex'
export default createStore({
state: {
user: null,
isLoading: false,
todos: [],
},
mutations: {
SET_USER(state, user) {
state.user = user
},
SET_LOADING(state, status) {
state.isLoading = status
},
ADD_TODO(state, todo) {
state.todos.push(todo)
},
},
actions: {
async login({ commit }, credentials) {
commit('SET_LOADING', true)
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials),
})
const user = await response.json()
commit('SET_USER', user)
} finally {
commit('SET_LOADING', false)
}
},
addTodo({ commit }, todo) {
commit('ADD_TODO', todo)
},
},
getters: {
isLoggedIn: (state) => state.user !== null,
pendingTodos: (state) => state.todos.filter(t => !t.completed),
},
modules: {
// 模块化组织
},
})在组件中使用:
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
// 通过 getters 访问派生状态
const isLoggedIn = computed(() => store.getters.isLoggedIn)
const pendingTodos = computed(() => store.getters.pendingTodos)
// 通过 actions 触发状态变更
const login = () => store.dispatch('login', {
email: '[email protected]',
password: 'password',
})
return { isLoggedIn, pendingTodos, login }
},
}
</script>核心概念与架构
Vuex 的架构遵循严格的单向数据流模式,由五个核心概念组成:
1. State(状态)
State 是整个应用的数据源,存储在 Store 中,是所有组件共享的"单一数据源"。Vue 的响应式系统会自动追踪 State 的变化,当 State 更新时,依赖该 State 的组件会自动重新渲染。
state: {
count: 0,
user: null,
todos: [],
}2. Getters(计算属性)
Getters 是 Store 的计算属性,用于从 State 派生出新的值。与 Vue 组件的 computed 类似,Getters 的结果会被缓存,只有依赖的 State 变化时才会重新计算。
getters: {
completedTodos: (state) => state.todos.filter(t => t.completed),
todoCount: (state, getters) => state.todos.length,
}3. Mutations(变更)
Mutations 是唯一可以直接修改 State 的方式,且必须是同步函数。每个 Mutation 都有一个字符串类型的事件类型(type)和一个回调函数(handler),回调函数接收 State 作为第一个参数。
mutations: {
increment(state) {
state.count++
},
// 支持 payload 参数
incrementBy(state, amount) {
state.count += amount
},
}
// 调用方式
store.commit('increment')
store.commit('incrementBy', 10)4. Actions(操作)
Actions 用于处理异步操作(如 API 调用),不能直接修改 State,而是通过提交(commit)Mutation 来间接修改。Actions 可以包含任意异步操作。
actions: {
async fetchTodos({ commit }) {
const response = await fetch('/api/todos')
const todos = await response.json()
commit('SET_TODOS', todos)
},
}
// 调用方式
store.dispatch('fetchTodos')5. Modules(模块)
当应用规模增大时,Store 可能变得庞大。Modules 允许将 Store 分割成多个模块,每个模块拥有自己的 State、Mutations、Actions、Getters,甚至嵌套子模块——"fractal all the way down"。
const store = createStore({
modules: {
auth: {
namespaced: true,
state: () => ({ user: null }),
mutations: {
SET_USER(state, user) { state.user = user }
},
actions: {
login({ commit }, credentials) { /* ... */ }
},
getters: {
isLoggedIn: (state) => state.user !== null
},
},
cart: {
namespaced: true,
state: () => ({ items: [] }),
mutations: {
ADD_ITEM(state, item) { state.items.push(item) }
},
},
},
})
// 访问命名空间模块
store.commit('auth/SET_USER', userData)
store.getters['auth/isLoggedIn']架构流程
组件 View → dispatch(action) → action(异步)→ commit(mutation) → mutation(同步)→ 修改 State → 响应式更新组件
核心特性
-
🏛️ 单一数据源(Single Source of Truth):整个应用的全局状态集中存储在一个 Store 中,确保状态的可预测性和一致性,避免了分散状态导致的同步困难。
-
🔒 严格的单向数据流:状态变更必须通过 Mutation → Action 的固定路径进行,保证了状态变更的可追踪性和可调试性,方便进行时间旅行调试。
-
🧩 模块化(Modules)系统:支持将 Store 按功能拆分为多个命名空间模块,每个模块拥有独立的 State、Getters、Mutations、Actions,甚至可以嵌套子模块,实现"分形"结构。
-
🛠️ DevTools 深度集成:与 Vue DevTools 无缝集成,提供时间旅行调试(time-travel debugging)、状态快照导入/导出、Mutation 调用历史追踪等功能。
-
⚡ 严格模式(Strict Mode):开启严格模式后,任何不通过 Mutation 直接修改 State 的操作都会抛出错误,帮助开发者遵循最佳实践。
-
🔄 插件机制:支持通过插件扩展 Store 功能,Vuex 社区发展出了
vuex-persistedstate(状态持久化)、vuex-router-sync(路由同步)等实用插件。 -
📦 SSR 支持:支持服务端渲染,通过
store.state序列化和客户端水合实现 SSR 状态传递(但在 Nuxt 3 中已被 Pinia 替代)。 -
🧪 良好的可测试性:Store 的 Mutations、Actions、Getters 均为纯函数或可独立调用的函数,便于编写单元测试。
生态图
Vuex 的生态在其活跃期曾非常丰富,但目前已停止扩展:
| 生态项目 | 说明 |
|---|---|
| Vuex 4 | Vue 3 版本的 Vuex,当前版本 v4.1.0(维护模式) |
| Vuex 3 | Vue 2 版本的 Vuex(维护模式) |
| vuex-persistedstate | 最流行的状态持久化插件 |
| vuex-router-sync | 将 Vue Router 状态同步到 Vuex(已归档) |
| vuex-module-decorators | TypeScript 装饰器风格的 Vuex 模块定义 |
| vuex-composition-helpers | Composition API 风格的 Vuex 辅助函数 |
| vuex-pathify | 简化 Vuex 读写的路径式 API |
| vuex-orm | 基于 Vuex 的 ORM 库(已迁移到 Pinia ORM) |
| typed-vuex | 为 Vuex 添加完整 TypeScript 类型的辅助库 |
适用场景
-
Vue 2 存量项目维护:大量 Vue 2 项目使用 Vuex 3 作为状态管理方案,在无法迁移到 Vue 3 的情况下,Vuex 3 仍然是唯一选择。
-
Vue 3 遗留项目维护:部分早期 Vue 3 项目使用 Vuex 4,在 Pinia 成熟之前已经深度集成了 Vuex,短期内需要继续维护。
-
需要严格单向数据流的应用:Vuex 强制通过 Mutation 修改 State 的设计,在需要严格追踪状态变更来源的企业级应用中有一定价值。
-
需要时间旅行调试的场景:Vuex DevTools 的时间旅行功能可以回放任意状态变更历史,适合需要精确调试复杂状态流转的应用。
-
团队已有 Vuex 经验:如果团队对 Vuex 有深厚经验且项目不需要迁移,继续使用 Vuex 4 可以避免迁移成本,但新项目仍建议采用 Pinia。
-
教学与学习 Vue 状态管理:Vuex 作为经典的状态管理模式,其核心概念(单一数据源、单向数据流、Mutations/Actions 分离)对理解状态管理原理有重要的学习价值。
-
与旧版第三方库兼容:部分基于 Vuex 构建的第三方库(如旧版 Vuex ORM)可能尚未提供 Pinia 版本,需要继续使用 Vuex。
开发与工程化
TypeScript 支持
Vuex 4 的 TypeScript 支持较为繁琐,需要通过模块声明(Module Augmentation)手动注入类型:
// store/index.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
export interface State {
count: number
user: User | null
}
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
state: {
count: 0,
user: null,
},
mutations: {
SET_COUNT(state, count: number) {
state.count = count
},
},
})// 在组件中使用
import { useStore } from 'vuex'
import { key } from '@/store'
const store = useStore(key) // 获得完整类型推导这种手动声明类型的方式被广泛认为是 Vuex 在 TypeScript 项目中的主要痛点,也是 Pinia 被推荐的重要原因之一。
模块化组织
大型应用推荐按功能模块组织 Store 结构:
store/
├── index.ts # Store 入口,组合所有模块
├── modules/
│ ├── auth/
│ │ ├── index.ts # 认证模块 Store
│ │ ├── state.ts
│ │ ├── mutations.ts
│ │ ├── actions.ts
│ │ └── getters.ts
│ ├── cart/
│ │ ├── index.ts
│ │ ├── state.ts
│ │ ├── mutations.ts
│ │ ├── actions.ts
│ │ └── getters.ts
│ └── products/
│ └── ...
└── types.ts # 全局类型定义
严格模式
在开发环境中开启严格模式,确保所有状态变更都通过 Mutation:
const store = createStore({
// ...
strict: process.env.NODE_ENV !== 'production',
})插件开发
// 自定义 Vuex 插件示例:状态变更日志
const myPlugin = (store) => {
store.subscribe((mutation, state) => {
console.log(`Mutation: ${mutation.type}`, mutation.payload)
console.log('New state:', state)
})
}
const store = createStore({
plugins: [myPlugin],
// ...
})状态持久化
使用 vuex-persistedstate 实现跨页面状态持久化:
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'
const store = createStore({
plugins: [createPersistedState({
storage: window.localStorage,
paths: ['auth.token', 'settings.theme'],
})],
state: {
auth: { token: '' },
settings: { theme: 'light' },
},
})性能与安全
性能特性
- 响应式追踪:基于 Vue 的响应式系统,State 变更自动触发依赖组件更新
- Getter 缓存:Getters 具有缓存特性,避免重复计算
- 模块化代码分割:通过
modules配合动态导入可实现部分代码分割 - 体积较大:Vuex 4 约 6KB gzipped,相比 Pinia 的 1.5KB 体积更大
- 辅助映射函数:
mapState、mapGetters、mapActions等辅助函数会引入额外的模块引用
安全考量
- 状态暴露风险:Store 中的 State 存储在客户端内存中,敏感数据(如认证 Token)仍可通过浏览器 DevTools 查看
- SSR 状态序列化:使用 SSR 时,State 会通过
<script>标签序列化到 HTML 中,注意不要存储敏感信息 - Mutation 可追踪性:严格模式确保所有状态变更通过 Mutation,便于审计和安全追踪
- XSS 风险:Store 中的数据直接渲染到模板时,需注意 Vue 的转义机制,避免 XSS 攻击
- 第三方插件风险:使用第三方 Vuex 插件时需注意其安全性,特别是涉及 State 读写的插件
技术对比
| 特性 | Vuex 4 | Pinia | Redux | MobX | Zustand |
|---|---|---|---|---|---|
| 适用框架 | Vue 3 | Vue 3 | React/通用 | 框架无关 | React/通用 |
| 包体积 | ~6KB | ~1.5KB | ~7KB | ~16KB | ~1KB |
| TypeScript 支持 | ⚠️ 需手动声明 | ✅ 完整推导 | ⚠️ 需样板代码 | ✅ 良好 | ✅ 良好 |
| Composition API | ❌ 不原生支持 | ✅ 原生支持 | ❌ 不支持 | ⚠️ 部分支持 | ❌ 不支持 |
| Mutations 强制 | ✅ 必须同步 Mutation | ❌ 已移除 | ❌ 无 | ❌ 无 | ❌ 无 |
| 模块系统 | modules + namespaced | 多 Store 天然模块化 | slices | 无原生模块 | 多 Store |
| DevTools 集成 | ✅ 完整 | ✅ 完整 | ✅ 完整 | ✅ 完整 | ✅ 基本 |
| Vue 官方推荐 | ❌ 否(维护模式) | ✅ 是(当前默认) | ❌ 否 | ❌ 否 | ❌ 否 |
| 学习曲线 | 中高 | 低 | 高 | 中 | 低 |
| 未来发展方向 | 仅维护,无新功能 | 积极发展中 | 积极发展 | 积极发展 | 积极发展 |
| 社区活跃度 | 下降中 | 高 | 高 | 高 | 高 |
Vuex 与 Pinia 核心差异
| 对比维度 | Vuex 4 | Pinia |
|---|---|---|
| API 设计 | mutations + actions 二元分离 | 仅 actions,直接修改 State |
| 模块化 | modules + namespaced(繁琐) | 多 Store(简洁直观) |
| 类型推导 | 需手动 InjectionKey + 接口声明 | 自动推导,开箱即用 |
| 代码量 | 大量样板代码 | 极少样板代码 |
| 组件中使用 | mapState/mapActions 辅助函数 | 直接调用 Store 方法 |
| Store 间通信 | 需 rootState/rootGetters 间接访问 | 直接引用其他 Store |
| 热更新 | 有限支持 | 完整 HMR 支持 |
| 插件系统 | 基础插件机制 | 更灵活的插件 API |
最佳实践
1. 按功能模块组织 Store
避免将所有状态放在一个巨大的 State 对象中,使用 modules 按功能边界拆分:
// ✅ 推荐:模块化组织
const store = createStore({
modules: {
auth: authModule,
cart: cartModule,
products: productsModule,
},
})
// ❌ 避免:单一巨型 Store
const store = createStore({
state: { /* 几十个字段混在一起 */ },
mutations: { /* 上百个 mutation */ },
})2. 使用命名空间模块
避免不同模块的 Mutation/Action 类型冲突:
// ✅ 开启 namespaced
const cartModule = {
namespaced: true,
state: () => ({ items: [] }),
mutations: {
ADD_ITEM(state, item) { state.items.push(item) },
},
}
// 调用时必须带命名空间前缀
store.commit('cart/ADD_ITEM', newItem)
store.dispatch('cart/fetchCartItems')3. Mutations 必须保持同步
mutations: {
// ✅ 正确:同步操作
SET_USER(state, user) {
state.user = user
},
// ❌ 错误:异步操作会导致 DevTools 无法追踪
async SET_USER(state, user) {
await someAsyncOperation()
state.user = user
},
}4. 合理使用 Getters 缓存
getters: {
// ✅ 适合复杂计算
expensiveStats: (state) => {
return state.items.reduce((acc, item) => {
return acc + item.value * item.weight
}, 0)
},
// ✅ 组合 Getters
statsSummary: (state, getters) => ({
count: state.items.length,
total: getters.expensiveStats,
}),
}5. Actions 中处理错误
actions: {
async fetchData({ commit }) {
commit('SET_LOADING', true)
try {
const data = await api.fetchData()
commit('SET_DATA', data)
} catch (error) {
commit('SET_ERROR', error.message)
throw error // 让调用方也能处理错误
} finally {
commit('SET_LOADING', false)
}
},
}6. 在 Composition API 中使用 useStore
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
const increment = () => store.commit('increment')
const fetchAsync = () => store.dispatch('fetchData')
</script>技术局限与边界
-
维护模式,不再发展:Vuex 已被 Vue 官方明确标记为维护模式,不会再添加新功能。Vuex 5 的 RFC 已关闭,Pinia 成为官方替代方案。
-
TypeScript 支持薄弱:Vuex 4 的 TypeScript 类型推导非常繁琐,需要手动声明
InjectionKey、State 接口,复杂嵌套类型的推导困难,是开发者最大的痛点。 -
Mutations 强制同步的限制:虽然强制同步有利于调试,但也增加了代码量——每个异步操作都需要 Action + Mutation 的组合,导致大量样板代码。
-
命名空间模块的复杂性:
namespaced: true的配置和commit('module/action')的字符串调用方式容易出错,类型推导几乎失效,在大型项目中维护成本高。 -
不支持 Composition API 风格:Vuex 4 的 API 设计基于 Options API,在 Composition API 中使用需要借助
useStore()和辅助映射函数,开发体验不如 Pinia 原生。 -
包体积较大:Vuex 4 约 6KB gzipped,相比 Pinia 的 1.5KB 大了近 4 倍。
-
辅助映射函数的局限:
mapState、mapGetters、mapActions等辅助函数在 Composition API 中使用不自然,且类型推导差。 -
社区生态萎缩:随着开发者转向 Pinia,Vuex 相关的新教程、新插件、新工具越来越少,长期来看生态将持续萎缩。
-
无内置持久化:状态持久化需要第三方插件(
vuex-persistedstate),且该插件的维护活跃度也在下降。 -
迁移成本:虽然 Vuex 和 Pinia 可以共存,但从 Vuex 迁移到 Pinia 仍需要一定的重构工作,特别是在深度使用命名空间模块和辅助映射函数的项目中。
学习资源
官方资源
社区资源
- vuex-persistedstate 文档 — 状态持久化
- vuex-module-decorators 文档 — TypeScript 装饰器
- Vuex Module Pattern 最佳实践
- Vue.js State Management 对比文章
中文资源
迁移资源
- 从 Vuex 迁移到 Pinia 完全指南 — 2026 年最新迁移指南
- Migrating from Vuex ≤4(Pinia 官方)
- Migrating Vue 2 to 3: Vuex to Pinia(Medium)
2026 年现状
截至 2026 年,Vuex 的定位已经非常明确:
-
官方维护模式:Vuex 3(Vue 2)和 Vuex 4(Vue 3)仍保持维护,但不会添加任何新功能。官方文档已明确标注"Pinia is now the new default",并将用户引导至 Pinia。
-
Vuex 5 RFC 已关闭:Vue 团队曾讨论 Vuex 5 的设计,但最终因 Pinia 的成熟而关闭了 RFC,明确表示"Pinia has almost the exact same or enhanced API as Vuex 5"。Pinia 实质上就是 Vuex 的精神继承者。
-
存量项目仍在使用:大量 Vue 2 项目(使用 Vuex 3)和早期 Vue 3 项目(使用 Vuex 4)仍在运行中。这些项目不需要立即迁移,但建议在版本升级计划中纳入向 Pinia 的迁移。
-
社区资源转移:2025-2026 年的新技术文章、视频教程和面试资料几乎都以 Pinia 为主。Vuex 的社区讨论热度持续下降,新的 Vuex 插件和工具也已基本停止开发。
-
Vue 生态全面转向 Pinia:
create-vue官方脚手架默认集成 Pinia,Nuxt 3 默认集成 Pinia,Vue DevTools 对 Pinia 的支持最为完善。在 Vue 生态的方方面面,Pinia 已经完全取代 Vuex 成为标准配置。 -
渐进式迁移成为共识:Vuex 和 Pinia 可以在同一项目中共存,社区推荐采用渐进式迁移策略——新模块使用 Pinia,旧模块逐步迁移,避免一次性大规模重构的风险。
-
历史地位不可磨灭:作为 Vue 生态发展过程中最重要的状态管理库之一,Vuex 的"单一数据源"、"严格单向数据流"、"模块化"等核心理念深刻影响了 Vue 开发者的思维方式和架构实践。这些理念也在 Pinia 中得到了继承和发展。