从 Vuex 迁移到 Pinia 采用渐进式替换:先共存(新功能用 Pinia、旧模块保留 Vuex),再逐模块迁移(状态转 ref/reactive、getter 改 computed、action 去 mutation),最后清理;Pinia 结构扁平、类型友好、原生支持模块化与 TS。

从 Vuex 迁移到 Pinia 不需要推倒重来,核心是“渐进式替换”——先共存,再收敛,最后清理。Pinia 设计更贴近 Composition API,状态、getter、action 都封装在 store 中,没有 mutations 的概念,结构更扁平、类型推导更友好。
1. 保持 Vuex 和 Pinia 并行运行
迁移初期不要急于移除 Vuex。可以在项目中同时使用两者:新功能用 Pinia,老模块维持 Vuex,避免一次性大改带来的风险。
- 安装 Pinia:
npm install pinia,并在 main.js 中启用(app.use(createPinia())) - Vuex store 保持原样,不改动;新建的 store 全部用
defineStore创建 - 组件中可混合使用:
useStore()(Pinia)和mapState(Vuex),互不影响
2. 逐个模块迁移:从最简单 store 开始
选一个逻辑独立、依赖少的 Vuex module(比如 user 或 theme),按三步重构:
-
状态(state):直接转为
ref或reactive,初始值写在return中 -
Getter:改写为
computed属性,用storeToRefs解构时自动响应 - Action:函数体不变,直接定义在 store 内,支持 async/await,无需 commit/mutation
例如 Vuex 中的 user/setToken action,迁移到 Pinia 后变成:setToken(token) { this.token = token },简洁直观。
立即学习“前端免费学习笔记(深入)”;
3. 处理 Vuex 特有模式:modules、命名空间、插件
Pinia 原生支持模块化(每个 defineStore 就是一个模块),无需 namespaced: true,也不用 createNamespacedHelpers。
- 动态注册?Pinia 支持
defineStore(id, options)+useStore()按需调用,比registerModule更自然 - 持久化?去掉
vuex-persistedstate,改用pinia-plugin-persistedstate,配置方式类似但更轻量 - 全局状态监听?用
store.$subscribe替代store.watch,参数结构更清晰
4. 类型与测试:利用 Pinia 的 TypeScript 友好性
Pinia 对 TS 支持开箱即用,store 接口自推导,不需要额外声明类型或 mapState 映射。
- 定义 store 时,state 类型由初始值自动推断;也可显式标注接口,如
state: () => UserState - 组件中使用
const { count } = storeToRefs(useCounter()),count 类型精准,编辑器能提示 - 单元测试更简单:直接 new store 实例,调用 action 后断言 state,无需 mock store 或 dispatch
迁移不是重写,而是借 Pinia 的设计让状态逻辑回归自然表达。旧代码不动,新逻辑用新范式,等所有模块都覆盖后,再统一移除 Vuex 相关依赖和入口。不复杂但容易忽略细节。


















