Vuex logo

Vuex

Vue.js 的经典集中式状态管理库,曾作为 Vue 生态的核心状态管理方案,现已进入维护模式。

状态管理VueLegacy维护模式

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 → 响应式更新组件

核心特性

  1. 🏛️ 单一数据源(Single Source of Truth):整个应用的全局状态集中存储在一个 Store 中,确保状态的可预测性和一致性,避免了分散状态导致的同步困难。

  2. 🔒 严格的单向数据流:状态变更必须通过 Mutation → Action 的固定路径进行,保证了状态变更的可追踪性和可调试性,方便进行时间旅行调试。

  3. 🧩 模块化(Modules)系统:支持将 Store 按功能拆分为多个命名空间模块,每个模块拥有独立的 State、Getters、Mutations、Actions,甚至可以嵌套子模块,实现"分形"结构。

  4. 🛠️ DevTools 深度集成:与 Vue DevTools 无缝集成,提供时间旅行调试(time-travel debugging)、状态快照导入/导出、Mutation 调用历史追踪等功能。

  5. ⚡ 严格模式(Strict Mode):开启严格模式后,任何不通过 Mutation 直接修改 State 的操作都会抛出错误,帮助开发者遵循最佳实践。

  6. 🔄 插件机制:支持通过插件扩展 Store 功能,Vuex 社区发展出了 vuex-persistedstate(状态持久化)、vuex-router-sync(路由同步)等实用插件。

  7. 📦 SSR 支持:支持服务端渲染,通过 store.state 序列化和客户端水合实现 SSR 状态传递(但在 Nuxt 3 中已被 Pinia 替代)。

  8. 🧪 良好的可测试性:Store 的 Mutations、Actions、Getters 均为纯函数或可独立调用的函数,便于编写单元测试。

生态图

Vuex 的生态在其活跃期曾非常丰富,但目前已停止扩展:

生态项目说明
Vuex 4Vue 3 版本的 Vuex,当前版本 v4.1.0(维护模式)
Vuex 3Vue 2 版本的 Vuex(维护模式)
vuex-persistedstate最流行的状态持久化插件
vuex-router-sync将 Vue Router 状态同步到 Vuex(已归档)
vuex-module-decoratorsTypeScript 装饰器风格的 Vuex 模块定义
vuex-composition-helpersComposition API 风格的 Vuex 辅助函数
vuex-pathify简化 Vuex 读写的路径式 API
vuex-orm基于 Vuex 的 ORM 库(已迁移到 Pinia ORM)
typed-vuex为 Vuex 添加完整 TypeScript 类型的辅助库

适用场景

  1. Vue 2 存量项目维护:大量 Vue 2 项目使用 Vuex 3 作为状态管理方案,在无法迁移到 Vue 3 的情况下,Vuex 3 仍然是唯一选择。

  2. Vue 3 遗留项目维护:部分早期 Vue 3 项目使用 Vuex 4,在 Pinia 成熟之前已经深度集成了 Vuex,短期内需要继续维护。

  3. 需要严格单向数据流的应用:Vuex 强制通过 Mutation 修改 State 的设计,在需要严格追踪状态变更来源的企业级应用中有一定价值。

  4. 需要时间旅行调试的场景:Vuex DevTools 的时间旅行功能可以回放任意状态变更历史,适合需要精确调试复杂状态流转的应用。

  5. 团队已有 Vuex 经验:如果团队对 Vuex 有深厚经验且项目不需要迁移,继续使用 Vuex 4 可以避免迁移成本,但新项目仍建议采用 Pinia。

  6. 教学与学习 Vue 状态管理:Vuex 作为经典的状态管理模式,其核心概念(单一数据源、单向数据流、Mutations/Actions 分离)对理解状态管理原理有重要的学习价值。

  7. 与旧版第三方库兼容:部分基于 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 体积更大
  • 辅助映射函数mapStatemapGettersmapActions 等辅助函数会引入额外的模块引用

安全考量

  • 状态暴露风险:Store 中的 State 存储在客户端内存中,敏感数据(如认证 Token)仍可通过浏览器 DevTools 查看
  • SSR 状态序列化:使用 SSR 时,State 会通过 &lt;script&gt; 标签序列化到 HTML 中,注意不要存储敏感信息
  • Mutation 可追踪性:严格模式确保所有状态变更通过 Mutation,便于审计和安全追踪
  • XSS 风险:Store 中的数据直接渲染到模板时,需注意 Vue 的转义机制,避免 XSS 攻击
  • 第三方插件风险:使用第三方 Vuex 插件时需注意其安全性,特别是涉及 State 读写的插件

技术对比

特性Vuex 4PiniaReduxMobXZustand
适用框架Vue 3Vue 3React/通用框架无关React/通用
包体积~6KB~1.5KB~7KB~16KB~1KB
TypeScript 支持⚠️ 需手动声明✅ 完整推导⚠️ 需样板代码✅ 良好✅ 良好
Composition API❌ 不原生支持✅ 原生支持❌ 不支持⚠️ 部分支持❌ 不支持
Mutations 强制✅ 必须同步 Mutation❌ 已移除❌ 无❌ 无❌ 无
模块系统modules + namespaced多 Store 天然模块化slices无原生模块多 Store
DevTools 集成✅ 完整✅ 完整✅ 完整✅ 完整✅ 基本
Vue 官方推荐❌ 否(维护模式)✅ 是(当前默认)❌ 否❌ 否❌ 否
学习曲线中高
未来发展方向仅维护,无新功能积极发展中积极发展积极发展积极发展
社区活跃度下降中

Vuex 与 Pinia 核心差异

对比维度Vuex 4Pinia
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>

技术局限与边界

  1. 维护模式,不再发展:Vuex 已被 Vue 官方明确标记为维护模式,不会再添加新功能。Vuex 5 的 RFC 已关闭,Pinia 成为官方替代方案。

  2. TypeScript 支持薄弱:Vuex 4 的 TypeScript 类型推导非常繁琐,需要手动声明 InjectionKey、State 接口,复杂嵌套类型的推导困难,是开发者最大的痛点。

  3. Mutations 强制同步的限制:虽然强制同步有利于调试,但也增加了代码量——每个异步操作都需要 Action + Mutation 的组合,导致大量样板代码。

  4. 命名空间模块的复杂性namespaced: true 的配置和 commit('module/action') 的字符串调用方式容易出错,类型推导几乎失效,在大型项目中维护成本高。

  5. 不支持 Composition API 风格:Vuex 4 的 API 设计基于 Options API,在 Composition API 中使用需要借助 useStore() 和辅助映射函数,开发体验不如 Pinia 原生。

  6. 包体积较大:Vuex 4 约 6KB gzipped,相比 Pinia 的 1.5KB 大了近 4 倍。

  7. 辅助映射函数的局限mapStatemapGettersmapActions 等辅助函数在 Composition API 中使用不自然,且类型推导差。

  8. 社区生态萎缩:随着开发者转向 Pinia,Vuex 相关的新教程、新插件、新工具越来越少,长期来看生态将持续萎缩。

  9. 无内置持久化:状态持久化需要第三方插件(vuex-persistedstate),且该插件的维护活跃度也在下降。

  10. 迁移成本:虽然 Vuex 和 Pinia 可以共存,但从 Vuex 迁移到 Pinia 仍需要一定的重构工作,特别是在深度使用命名空间模块和辅助映射函数的项目中。

学习资源

官方资源

社区资源

中文资源

迁移资源

2026 年现状

截至 2026 年,Vuex 的定位已经非常明确:

  1. 官方维护模式:Vuex 3(Vue 2)和 Vuex 4(Vue 3)仍保持维护,但不会添加任何新功能。官方文档已明确标注"Pinia is now the new default",并将用户引导至 Pinia。

  2. Vuex 5 RFC 已关闭:Vue 团队曾讨论 Vuex 5 的设计,但最终因 Pinia 的成熟而关闭了 RFC,明确表示"Pinia has almost the exact same or enhanced API as Vuex 5"。Pinia 实质上就是 Vuex 的精神继承者。

  3. 存量项目仍在使用:大量 Vue 2 项目(使用 Vuex 3)和早期 Vue 3 项目(使用 Vuex 4)仍在运行中。这些项目不需要立即迁移,但建议在版本升级计划中纳入向 Pinia 的迁移。

  4. 社区资源转移:2025-2026 年的新技术文章、视频教程和面试资料几乎都以 Pinia 为主。Vuex 的社区讨论热度持续下降,新的 Vuex 插件和工具也已基本停止开发。

  5. Vue 生态全面转向 Piniacreate-vue 官方脚手架默认集成 Pinia,Nuxt 3 默认集成 Pinia,Vue DevTools 对 Pinia 的支持最为完善。在 Vue 生态的方方面面,Pinia 已经完全取代 Vuex 成为标准配置。

  6. 渐进式迁移成为共识:Vuex 和 Pinia 可以在同一项目中共存,社区推荐采用渐进式迁移策略——新模块使用 Pinia,旧模块逐步迁移,避免一次性大规模重构的风险。

  7. 历史地位不可磨灭:作为 Vue 生态发展过程中最重要的状态管理库之一,Vuex 的"单一数据源"、"严格单向数据流"、"模块化"等核心理念深刻影响了 Vue 开发者的思维方式和架构实践。这些理念也在 Pinia 中得到了继承和发展。