Pinia vs Vuex 核心区别完整对比(同步/异步/数据/优势)
本文结合 Vue 生态最新趋势,梳理 Pinia(Vue3 官方推荐) 与 Vuex(Vue2 经典状态管理) 在「同步方法调用、异步方法调用、数据结构、核心优势」上的区别,附代码示例和对比表,适合直接记到 CSDN 或作为开发速查表。
一、先明确:两者的核心定位
| Vuex | 2015年(Vue2 时代) | Vue2 官方状态管理库(Vue3 也支持但非首选) | State(状态)、Mutations(同步修改)、Actions(异步处理)、Getters(计算属性)、Modules(模块化,需手动开启 namespaced) |
| Pinia | 2019年(Vue3 时代) | Vue3 官方推荐状态管理库(Vue2 也支持需额外配置) | State(状态)、Getters(计算属性)、Actions(同步/异步统一处理)、天然模块化(无 Mutations,无需 namespaced) |
二、核心区别1:同步方法(修改 State)
核心差异
- Vuex:必须通过 Mutations 同步修改 State,组件/ Actions 中用 commit 调用;
- Pinia:废弃了 Mutations,直接在 Actions 或组件中修改 State,无需额外调用。
代码对比(结合 Token 场景)
Vuex 同步修改 State
// store/modules/user.js(Vuex)
export default {
namespaced: true,
state: {
token: ''
},
// Vuex 必须用 Mutations 同步修改 State
mutations: {
setToken(state, newToken) {
state.token = newToken
localStorage.setItem('token', newToken)
}
},
actions: {
// Actions 中通过 commit 调用 Mutations
async login(ctx, loginForm) {
const res = await loginAPI(loginForm)
ctx.commit('setToken', res.data.token) // 必须 commit
}
}
}
<!– Vuex 组件中修改 State –>
<script>
export default {
methods: {
handleUpdateToken() {
// 组件中通过 commit 调用 Mutations
this.$store.commit('user/setToken', '新的token值')
}
}
}
</script>
Pinia 同步修改 State
// stores/user.js(Pinia)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: ''
}),
actions: {
// Pinia Actions 中直接修改 State,无需 Mutations
setToken(newToken) {
this.token = newToken
localStorage.setItem('token', newToken)
}
}
})
<!– Pinia 组件中修改 State –>
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 方式1:直接调用 Actions
const handleUpdateToken = () => {
userStore.setToken('新的token值')
}
// 方式2:甚至可以直接在组件中修改 State(不推荐但可行,建议封装到 Actions)
const handleDirectUpdate = () => {
userStore.token = '直接修改的token值'
}
</script>
三、核心区别2:异步方法(处理业务逻辑)
核心差异
- Vuex:必须通过 Actions 处理异步,组件中用 dispatch 调用,Actions 内部再 commit Mutations;
- Pinia:Actions 天然支持 async/await,组件中直接调用 Actions(无需 dispatch),Actions 内部可直接修改 State。
代码对比(结合登录场景)
Vuex 异步处理
// store/modules/user.js(Vuex)
export default {
namespaced: true,
state: { token: '', userInfo: {} },
mutations: {
setToken(state, token) { state.token = token },
setUserInfo(state, info) { state.userInfo = info }
},
// Vuex Actions 处理异步,必须 commit Mutations
actions: {
async asynclogin(ctx, loginForm) {
// 1. 异步调登录接口
const res = await loginAPI(loginForm)
// 2. commit 修改 Token
ctx.commit('setToken', res.data.token)
// 3. 继续异步调用户信息接口
const userRes = await getUserInfoAPI()
// 4. commit 修改用户信息
ctx.commit('setUserInfo', userRes.data)
}
}
}
<!– Vuex 组件中调用异步 –>
<script>
export default {
methods: {
async handleLogin() {
// 组件中通过 dispatch 调用 Actions
await this.$store.dispatch('user/asynclogin', this.form)
this.$router.replace('/')
}
}
}
</script>
Pinia 异步处理
// stores/user.js(Pinia)
import { defineStore } from 'pinia'
import { loginAPI, getUserInfoAPI } from '@/api/user'
export const useUserStore = defineStore('user', {
state: () => ({ token: '', userInfo: {} }),
// Pinia Actions 直接支持 async/await,直接修改 State
actions: {
async asynclogin(loginForm) {
// 1. 异步调登录接口
const res = await loginAPI(loginForm)
// 2. 直接修改 Token
this.token = res.data.token
localStorage.setItem('token', this.token)
// 3. 继续异步调用户信息接口
const userRes = await getUserInfoAPI()
// 4. 直接修改用户信息
this.userInfo = userRes.data
}
}
})
<!– Pinia 组件中调用异步 –>
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 直接调用 Actions,无需 dispatch
const handleLogin = async () => {
await userStore.asynclogin(userStore.form)
router.replace('/')
}
</script>
四、核心区别3:数据结构与模块化
核心差异
- Vuex:单一状态树,Modules 嵌套深,需手动开启 namespaced: true 才能隔离模块;
- Pinia:天然模块化,每个 Store 独立定义(defineStore('模块名', {})),无需额外配置,模块间自动隔离。
代码对比
Vuex 模块化(需手动隔离)
// store/index.js(Vuex 入口)
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import home from './modules/home'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
// 必须手动开启 namespaced,否则模块会混在一起
user: { namespaced: true, …user },
home: { namespaced: true, …home }
}
})
Pinia 模块化(天然隔离)
// stores/user.js(Pinia user 模块)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', { /* … */ })
// stores/home.js(Pinia home 模块)
import { defineStore } from 'pinia'
export const useHomeStore = defineStore('home', { /* … */ })
// 组件中直接引入对应 Store,无需配置入口
import { useUserStore } from '@/stores/user'
import { useHomeStore } from '@/stores/home'
五、核心区别4:TypeScript 支持
核心差异
- Vuex:TypeScript 支持非常差,需要写大量类型定义,类型推导不友好;
- Pinia:原生完美支持 TypeScript,类型自动推导,无需额外配置(这是 Vue3 官方推荐 Pinia 的核心原因之一)。
六、优势劣势对比表(一目了然)
| 代码简洁度 | ✅ 无 Mutations、无 commit/dispatch,代码量减少 30%+ | ❌ Vue2 支持需额外配置(@vue/composition-api) | ✅ Vue2 生态成熟,老项目文档多 | ❌ 必须 Mutations、commit/dispatch,代码冗余 |
| 模块化 | ✅ 天然模块化,无需 namespaced | ❌ 无 | ✅ 支持大型项目深度嵌套(但复杂) | ❌ 需手动配置 namespaced,嵌套深了难维护 |
| TypeScript | ✅ 原生完美支持,类型自动推导 | ❌ 无 | ✅ 支持但需大量类型定义 | ❌ 类型推导差,配置复杂 |
| DevTools | ✅ 支持更好,可直接追踪 Actions 调用、State 变化 | ❌ 无 | ✅ 支持成熟 | ❌ 追踪 Mutations 不如 Pinia 直观 |
| 异步处理 | ✅ Actions 直接 async/await,直接改 State | ❌ 无 | ✅ 支持异步 | ❌ 必须 Actions → commit → Mutations,流程长 |
| 学习成本 | ✅ 极低,概念少,上手快 | ❌ 无 | ✅ 老项目资料多 | ❌ 概念多(Mutations、Actions、Modules、namespaced),学习成本高 |
七、适用场景总结
| Vue3 新项目 | ✅ Pinia(官方推荐,代码简洁,TS 支持好) |
| Vue2 老项目维护 | ✅ Vuex(生态成熟,无需重构) |
| 中小型项目、追求代码简洁 | ✅ Pinia |
| 大型项目、需要严格状态管理规范 | ✅ Pinia(天然模块化,也能满足规范)或 Vuex |
| 需要 TypeScript 支持 | ✅ Pinia(原生支持,体验最好) |
八、最终选择建议
- 如果你正在学 Vue3:直接用 Pinia,不用学 Vuex(除非要维护老项目);
- 如果你正在维护 Vue2 老项目:继续用 Vuex,不用重构;
- 如果你要写 CSDN 笔记/新项目:优先写 Pinia,这是未来趋势。
结合你之前的登录页、Token 场景,Pinia 的代码会比 Vuex 简洁很多,强烈推荐用 Pinia!



