前端八股7--- Vue 状态管理工具(vuex和pinia)

目录

  • 一、核心结论
  • 二、详细对比表
  • 三、核心概念对比
  • [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 的区别?

答:

  1. API 更简洁:Pinia 去掉了 mutations,只剩 state、getters、actions

  2. TypeScript 更好:Pinia 原生支持 TS,类型推导完美

  3. 体积更小:Pinia 非常轻量

  4. 模块化简单:Pinia 直接创建多个 store,无需 modules 配置

  5. 修改更方便: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
相关推荐
Geoking.2 小时前
后端Long型数据传到前端js后精度丢失的问题(前后端传输踩坑指南)
java·前端·javascript·后端
oi..2 小时前
CSRF安全攻防:Referer 校验与 Token 防护详解
前端·网络·笔记·测试工具·安全·网络安全·csrf
申耀的科技观察2 小时前
【观察】昂瑞微5G射频前端通过车规认证,筑牢智能网联汽车通信安全“底座”
前端·5g·汽车
qq_260241232 小时前
将盾CDN:Web应用防火墙(WAF)的工作原理与实战配置
前端·网络·安全
旺王雪饼 www2 小时前
《Express框架深度解析:从基础入门到高级实践与项目架构》
前端·node.js·express
时寒的笔记2 小时前
js7逆向案例_禁止f12打开&sojson打开
开发语言·javascript·ecmascript
stpzhf2 小时前
uniapp nvue组件多个text在一行并且高亮其中一些文字
前端·javascript·uni-app
十一.3662 小时前
003-004 虚拟DOM的两种创建方式、虚拟DOM与真实DOM
前端·javascript·html
三声三视2 小时前
React 18 并发渲染实战:useTransition、Suspense 与自动批处理深度解析
前端·javascript·react.js