Pinia 被 Vue 官方钦定为新一代状态管理方案,不是简单换个名字,而是针对 Vuex 长期被社区反馈的痛点做了彻底重构。如果你犹豫是否从 Vuex 迁移,下面这些差异就是最直接的决策依据。
不再有 Mutations:只剩 State、Getters、Actions
Vuex 中修改 state 必须通过 mutation,而 mutation 必须是同步函数,这催生了一种尴尬的代码模式:action 做异步请求,拿到结果后 commit 一个接收静态数据的 mutation 来更新 state。这层“中间人”在 Pinia 中直接被砍掉了。
// Vuex:action + mutation 两层跳转
actions: {
async fetchUser({ commit }) {
const user = await api.getUser()
commit('setUser', user)
}
},
mutations: {
setUser(state, user) {
state.user = user
}
}
// Pinia:action 直接修改 state
actions: {
async fetchUser() {
this.user = await api.getUser()
}
}
实际好处:代码量明显减少,逻辑链路缩短。对于新人来说,不再需要理解为什么“改数据要经过两道手续”。
完美的 TypeScript 支持,无需额外类型声明
Vuex 的类型推导一直是开发者的噩梦。为了让 $store.state.user.name 有正确的类型,往往需要手写复杂的类型声明文件或借助辅助函数,且一旦模块嵌套,推导链条极易断裂。
Pinia 从设计之初就以 TypeScript 为第一公民,所有状态的类型都能自动推导:
// 定义一个 Store,返回值类型自动成为 useStore 的返回类型
export const useUserStore = defineStore('user', () => {
const name = ref('')
const age = ref(0)
function setName(newName: string) {
name.value = newName
}
return { name, age, setName }
})
// 使用时,全部自动推断
const userStore = useUserStore()
userStore.name // 类型为 string
userStore.setName('Alice') // 参数类型提示为 string
不需要额外安装类型包,不需要写 d.ts 声明文件,你写的 JavaScript 逻辑本身就准确地反映了类型。这对大型 TypeScript 项目而言,可以省去大量类型维护成本。
扁平化模块设计,告别嵌套地狱
Vuex 的模块采用树形嵌套结构,通过 modules 字段层层递进。当项目规模变大时,访问深层模块的状态需要长长的路径:$store.state.moduleA.moduleB.someData。模块间的通信也常常依赖全局上下文 rootState,耦合度很高。
Pinia 取消了这种显式的模块嵌套。每个 Store 都是扁平的独立单元,通过文件系统自然组织:
stores/
user.js
cart.js
product.js
需要跨 Store 使用时,直接引入对应的 useXxxStore() 调用即可,就像使用普通的组合式函数一样:
// 在 cart Store 中使用 user Store
import { useUserStore } from './user'
export const useCartStore = defineStore('cart', () => {
const userStore = useUserStore()
const items = ref([])
function checkout() {
// 直接使用 userStore 的状态
api.submitOrder(userStore.token, items.value)
}
return { items, checkout }
})
实际好处:代码组织更灵活,模块间的依赖关系显式、可控,不再需要记忆“这个模块挂在哪个父模块下”。
更轻量、更直观
Pinia 核心压缩后仅约 1KB,API 数量极少,核心概念只有 State / Getters / Actions 三样。创建 Store 支持两种写法:
- Options Store:类似 Vuex 的对象配置,适合从 Vuex 迁移。
- Setup Store:用组合式 API 的方式定义,与 Vue 3 的
<script setup>心智模型完全一致。
而后一种写法让 Pinia 与 Vue 3 的响应式系统无缝融合:ref、computed、watch 直接就是 Pinia 的状态和计算属性。
内置 DevTools 支持,调试体验更佳
Pinia 与 Vue DevTools 深度集成,可以清晰看到所有 Store 的实时状态、调用过的 action 时间线,甚至支持时间旅行调试。对 Vuex 使用过的开发者来说,这种调试体验并不陌生,但 Pinia 做得更直观——你不需要再在 mutation 列表里翻找是哪次 commit 改了数据,直接在 action 的执行历史里定位问题。
不再有 namespace 的困扰
Vuex 模块默认会带上命名空间,访问时需要用 mapGetters('moduleName', ['getterName']) 这种冗长的字符串映射。如果不小心忘了加命名空间,状态就散落到全局,极难调试。Pinia 的每个 Store 天然独立,使用 useStore() 得到的对象本身就隔离了所有状态,没有“命名空间”的概念,代码写起来更自然。
一句话总结:Pinia 砍掉了 Vuex 中历史遗留的中间层和复杂模块概念,保留了响应式状态管理的核心能力,同时全面拥抱 TypeScript 和组合式 API。如果你新项目用 Vue 3,直接选 Pinia 就是最务实的选择;如果你还在用 Vuex,迁移到 Pinia 的性价比也极高——代码会少写很多,类型安全会好非常多。