目录
- 一、核心结论
- 二、详细对比表
- 三、核心概念对比
- [Vuex 的 5 个核心概念](#Vuex 的 5 个核心概念)
- [Pinia 的 3 个核心概念](#Pinia 的 3 个核心概念)
- 四、代码对比
- [Vuex 写法(Vue 3)](#Vuex 写法(Vue 3))
- [Pinia 写法(Vue 3)](#Pinia 写法(Vue 3))
- [五、Pinia 的优势详解](#五、Pinia 的优势详解)
- [1. API 更简洁](#1. API 更简洁)
- [2. TypeScript 友好](#2. TypeScript 友好)
- [3. 模块化更简单](#3. 模块化更简单)
- [4. 可以直接修改 state](#4. 可以直接修改 state)
- 六、实际使用建议
- [什么时候用 Pinia?](#什么时候用 Pinia?)
- [什么时候用 Vuex?](#什么时候用 Vuex?)
- [七、从 Vuex 迁移到 Pinia](#七、从 Vuex 迁移到 Pinia)
- 迁移对照表
- 迁移示例
- 八、面试高频问题
- [Q1:Pinia 和 Vuex 的区别?](#Q1:Pinia 和 Vuex 的区别?)
- [Q2:为什么 Pinia 要去掉 mutations?](#Q2:为什么 Pinia 要去掉 mutations?)
- [Q3:Vue 3 项目应该选 Pinia 还是 Vuex?](#Q3:Vue 3 项目应该选 Pinia 还是 Vuex?)
- 九、快速记忆
一、核心结论
Vue 官方状态管理工具:
-
Vuex:Vue2 时代的官方方案,Vue3 也支持但不再主推
-
Pinia:Vue3 时代的官方方案,目前主推
一句话: Vue3 项目用 Pinia,Vue2 项目用 Vuex。
二、详细对比表
| 对比维度 | Vuex (4.x) | Pinia |
|---|---|---|
| 适用版本 | Vue 2/3 | Vue 3 |
| API 复杂度 | 复杂(5个核心概念) | 简单(3个核心概念) |
| mutations | ✅ 有(必须通过它修改 state) | ❌ 无(直接修改) |
| actions | 只能写异步 | 同步+异步都可以 |
| TypeScript | 支持较弱,类型推导麻烦 | 原生支持,类型友好 |
| 模块化 | 需要 modules 配置 | 直接创建多个 store |
| 修改方式 | commit + dispatch | 直接调用或修改 |
| 体积 | 较大 | 轻量(~1KB) |
| DevTools | 支持 | 支持 |
| SSR | 支持 | 支持 |
| 官方定位 | 维护状态 | Vue3 主推 |
三、核心概念对比
Vuex 的 5 个核心概念
┌─────────────────────────────────────────────────────────┐
│ Vuex │
│ │
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ │
│ │ State │ │ Getters │ │ Actions │ │
│ │ (数据) │ │ (计算属性)│ │ (异步) │ │
│ └────┬────┘ └──────────┘ └────┬────┘ │
│ │ │ │
│ │ 只能通过 commit │ 通过 dispatch │
│ ↓ ↓ │
│ ┌─────────┐ ┌─────────┐ │
│ │Mutation │ │ Actions │ │
│ │(同步修改)│←─────────────────│ (可包含异步)│ │
│ └─────────┘ └─────────┘ │
│ │
│ 还有 Modules(模块化) │
└─────────────────────────────────────────────────────────┘
Pinia 的 3 个核心概念
┌─────────────────────────────────────────────────────────┐
│ Pinia │
│ │
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ │
│ │ State │ │ Getters │ │ Actions │ │
│ │ (数据) │ │ (计算属性)│ │(同步+异步)│ │
│ └────┬────┘ └──────────┘ └─────────┘ │
│ │ │ │
│ │ 直接修改 │ 直接调用 │
│ └──────────────────────────────┘ │
│ │
│ 无需 mutations,无需 modules,直接创建多个 store │
└─────────────────────────────────────────────────────────┘
四、代码对比
Vuex 写法(Vue 3)
javascript
// store/index.js
import { createStore } from 'vuex'
export default createStore({
// state:存放数据
state: {
count: 0,
user: {
name: '张三',
age: 18
}
},
// getters:计算属性
getters: {
doubleCount: (state) => state.count * 2,
userInfo: (state) => `${state.user.name} - ${state.user.age}岁`
},
// mutations:同步修改 state(唯一方式)
mutations: {
increment(state) {
state.count++
},
incrementBy(state, payload) {
state.count += payload
},
setUser(state, user) {
state.user = user
}
},
// actions:处理异步,提交 mutations
actions: {
async incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
},
async fetchUser({ commit }) {
const res = await api.getUser()
commit('setUser', res.data)
}
},
// modules:模块化
modules: {
cart: cartModule,
user: userModule
}
})
javascript
<!-- 组件中使用 Vuex -->
<template>
<div>
<p>count: {{ count }}</p>
<p>double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="incrementAsync">异步+1</button>
</div>
</template>
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'
const store = useStore()
// 读取 state
const count = computed(() => store.state.count)
// 读取 getters
const doubleCount = computed(() => store.getters.doubleCount)
// 修改 state:必须通过 commit 触发 mutation
function increment() {
store.commit('increment')
}
// 异步:必须通过 dispatch 触发 action
function incrementAsync() {
store.dispatch('incrementAsync')
}
</script>
Pinia 写法(Vue 3)
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// state:存放数据
state: () => ({
count: 0,
user: {
name: '张三',
age: 18
}
}),
// getters:计算属性
getters: {
doubleCount: (state) => state.count * 2,
userInfo: (state) => `${state.user.name} - ${state.user.age}岁`
},
// actions:同步+异步都可以
actions: {
// 同步
increment() {
this.count++ // 直接修改 this.count
},
incrementBy(amount) {
this.count += amount
},
// 异步
async incrementAsync() {
setTimeout(() => {
this.increment() // 可以调用其他 action
}, 1000)
},
async fetchUser() {
const res = await api.getUser()
this.user = res.data // 直接修改 state
}
}
})
javascript
<!-- 组件中使用 Pinia -->
<template>
<div>
<p>count: {{ counter.count }}</p>
<p>double: {{ counter.doubleCount }}</p>
<button @click="counter.increment()">+1</button>
<button @click="counter.incrementAsync()">异步+1</button>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
// 直接使用,无需 computed 包装
// 直接调用 action,无需 dispatch
// 可以直接修改 state(可选)
</script>
五、Pinia 的优势详解
1. API 更简洁
javascript
// Vuex:需要写很多样板代码
mutations: {
increment(state) { state.count++ },
decrement(state) { state.count-- },
setCount(state, value) { state.count = value }
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => commit('increment'), 1000)
}
}
// Pinia:直接写 actions,自动处理同步异步
actions: {
increment() { this.count++ },
decrement() { this.count-- },
setCount(value) { this.count = value },
incrementAsync() {
setTimeout(() => this.increment(), 1000)
}
}
2. TypeScript 友好
javascript
// Pinia:完美的 TS 支持
interface User {
name: string
age: number
}
export const useUserStore = defineStore('user', {
state: () => ({
user: null as User | null,
list: [] as User[]
}),
getters: {
userName: (state) => state.user?.name || '未登录'
},
actions: {
async fetchUser(id: number) {
const res = await api.getUser(id)
this.user = res.data // 类型自动推断
}
}
})
3. 模块化更简单
javascript
// Vuex:需要配置 modules
const store = createStore({
modules: {
cart: cartModule,
user: userModule,
product: productModule
}
})
// Pinia:直接创建多个 store,自动模块化
export const useCartStore = defineStore('cart', { ... })
export const useUserStore = defineStore('user', { ... })
export const useProductStore = defineStore('product', { ... })
4. 可以直接修改 state
javascript
// Pinia:可以直接修改(适合简单场景)
const store = useCounterStore()
store.count++ // ✅ 直接修改
// 也可以批量修改
store.$patch({
count: store.count + 1,
user: { name: '李四' }
})
// Vuex:必须 commit
store.commit('increment') // 必须通过 mutation
六、实际使用建议
什么时候用 Pinia?
| 场景 | 建议 |
|---|---|
| 新项目(Vue 3) | ✅ 推荐 Pinia |
| Vue 3 项目升级 | ✅ 推荐 Pinia |
| 需要 TypeScript | ✅ 推荐 Pinia |
| 小型项目 | ✅ 推荐 Pinia |
| 学习 Vue 3 | ✅ 推荐 Pinia |
什么时候用 Vuex?
| 场景 | 建议 |
|---|---|
| Vue 2 项目 | ✅ 推荐 Vuex |
| 老项目维护 | ✅ 继续用 Vuex |
| 团队熟悉 Vuex | 可以继续用 |
七、从 Vuex 迁移到 Pinia
迁移对照表
| Vuex | Pinia |
|---|---|
state |
state |
getters |
getters |
mutations |
不需要,直接修改 state |
actions |
actions(同步+异步) |
store.commit('mutation') |
store.action() 或直接修改 |
store.dispatch('action') |
store.action() |
modules |
多个 defineStore |
迁移示例
javascript
// Vuex
const store = new Vuex.Store({
state: { count: 0 },
mutations: { increment: state => state.count++ },
actions: { incrementAsync: ({ commit }) => setTimeout(() => commit('increment'), 1000) }
})
// 使用
store.commit('increment')
store.dispatch('incrementAsync')
// Pinia
const useStore = defineStore('main', {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++ },
incrementAsync() { setTimeout(() => this.increment(), 1000) }
}
})
// 使用
store.increment()
store.incrementAsync()
八、面试高频问题
Q1:Pinia 和 Vuex 的区别?
答:
-
API 更简洁:Pinia 去掉了 mutations,只剩 state、getters、actions
-
TypeScript 更好:Pinia 原生支持 TS,类型推导完美
-
体积更小:Pinia 非常轻量
-
模块化简单:Pinia 直接创建多个 store,无需 modules 配置
-
修改更方便:Pinia 可以直接修改 state,无需 commit
Q2:为什么 Pinia 要去掉 mutations?
答:
-
mutations 的设计是为了追踪状态变化,方便调试
-
但实际开发中,大部分 mutations 只是简单赋值,造成代码冗余
-
Pinia 直接修改 state 也能被 DevTools 追踪,且写法更简洁
-
同时支持
$patch批量修改,也能满足复杂场景
Q3:Vue 3 项目应该选 Pinia 还是 Vuex?
答:
-
推荐 Pinia,它是 Vue 3 官方主推的状态管理方案
-
Vuex 虽然也支持 Vue 3,但已不再主动添加新功能,只做维护
-
Pinia 更轻量、API 更友好、TS 支持更好
九、快速记忆
Vuex 五个概念:
state、getters、mutations、actions、modules
修改要 commit,异步要 dispatch
Pinia 三个概念:
state、getters、actions
直接修改,直接调用
新项目用 Pinia
老项目用 Vuex
Vue3 主推 Pinia
Vue2 只能 Vuex