第五章:Pinia 状态管理

Pinia 是 Vue 3 官方推荐的状态管理库,相比 Vuex 更轻量、类型推断更好、支持组合式 API 写法。


5.1 为什么用 Pinia

对比维度 Vuex 4 Pinia
TypeScript 支持 一般(需手动类型) 完整开箱即用
API 复杂度 mutation/action/module 只有 state/getters/actions
组合式 API 不支持 支持 setup store
DevTools 支持 支持(更好的时间旅行)
Bundle Size ~10KB ~1.5KB
代码分割 需要配置 天然支持

5.2 定义 Store

选项式写法(Options Store)

ts 复制代码
// stores/counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  // state:相当于 data
  state: () => ({
    count: 0,
    name: 'Counter'
  }),

  // getters:相当于 computed
  getters: {
    doubled: (state) => state.count * 2,
    isPositive: (state) => state.count > 0,
    // 引用其他 getter(使用 this)
    tripled(): number { return this.doubled * 1.5 }
  },

  // actions:相当于 methods,支持异步
  actions: {
    increment() { this.count++ },
    async fetchAndSet() {
      const data = await api.getData()
      this.count = data.count
    }
  }
})

组合式写法(Setup Store,推荐)

ts 复制代码
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  // ref → state
  const user = ref<User | null>(null)
  const token = ref(localStorage.getItem('token') || '')

  // computed → getters
  const isLoggedIn = computed(() => !!token.value)
  const userName = computed(() => user.value?.name ?? '游客')

  // function → actions
  async function login(email: string, password: string) {
    const data = await authApi.login({ email, password })
    token.value = data.token
    user.value = data.user
    localStorage.setItem('token', data.token)
  }

  function logout() {
    user.value = null
    token.value = ''
    localStorage.removeItem('token')
  }

  // 必须 return 所有东西
  return { user, token, isLoggedIn, userName, login, logout }
})

5.3 使用 Store

vue 复制代码
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()

// ✅ 用 storeToRefs 解构,保持响应式
const { count, doubled, isPositive } = storeToRefs(counterStore)

// ✅ actions 可以直接解构(不是响应式,无需 storeToRefs)
const { increment, fetchAndSet } = counterStore

// ❌ 错误做法:直接解构 state 失去响应式
// const { count } = counterStore  // count 不会更新!
</script>

<template>
  <p>{{ count }} × 2 = {{ doubled }}</p>
  <button @click="increment">+1</button>
  <button @click="counterStore.$patch({ count: 10 })">设为 10</button>
</template>

5.4 $patch 批量修改

ts 复制代码
const store = useUserStore()

// 方式1:对象 patch(简单场景)
store.$patch({
  name: '新名字',
  email: 'new@email.com'
})

// 方式2:函数 patch(推荐,支持复杂操作)
store.$patch((state) => {
  state.list.push({ id: Date.now(), text: '新条目' })
  state.count++
})

5.5 持久化存储

ts 复制代码
// 方案一:手动
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'

export const useSettingsStore = defineStore('settings', () => {
  const theme = ref(localStorage.getItem('theme') || 'dark')

  watch(theme, (val) => localStorage.setItem('theme', val))

  return { theme }
})

// 方案二:pinia-plugin-persistedstate 插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
pinia.use(piniaPluginPersistedstate)

defineStore('user', { /* ... */ }, {
  persist: {
    key: 'user-store',
    storage: localStorage,
    paths: ['token', 'preferences']  // 只持久化指定字段
  }
})

5.6 Store 间相互调用

ts 复制代码
// stores/cart.ts
import { useUserStore } from './user'

export const useCartStore = defineStore('cart', () => {
  const userStore = useUserStore()  // 直接调用其他 store

  async function checkout() {
    if (!userStore.isLoggedIn) {
      throw new Error('请先登录')
    }
    // ...
  }

  return { checkout }
})

章节总结

知识点 核心要点 重要程度
defineStore 组合式写法更推荐,灵活性更高 ⭐⭐⭐⭐⭐
storeToRefs 解构 state/getters 时必须用 ⭐⭐⭐⭐⭐
$patch 批量修改,函数形式支持复杂操作 ⭐⭐⭐⭐
持久化 手动 or pinia-plugin-persistedstate ⭐⭐⭐⭐
Store 间调用 直接在 action 中引用其他 store ⭐⭐⭐⭐

Demo 说明

Demo 位于 vue_demos/src/views/Chapter05_Pinia/PiniaDemo.vue

  • Counter Store(组合式):storeToRefs、increment/reset、变更历史
  • User Store(选项式):登录/登出、Getters 推导值展示
  • Cart Store:购物车增删、购物车角标跨组件共享(App Header)

🔗 专栏链接Vue 3 全栈开发实战专栏

📦 项目源码资源点击下载项目源码

相关推荐
小葛要努力9 天前
安装nvm 管理node.js版本实现vue2和vue3项目共存
node.js·vue·nvm
这里是杨杨吖10 天前
SpringBoot+Vue高校在线考试系统 附带详细运行指导视频
vue·在线考试·springboot
wuxia211810 天前
在5种环境中编写点击元素改变内容和颜色的JavaScript程序
javascript·微信小程序·vue·jquery·react
Sweet锦10 天前
Vue3 集成 ApexCharts 避坑指南:从动画失效到自定义指令的完美解决方案
vue·echarts
王小王-12311 天前
基于深度学习的个性化音乐推荐系统的设计与开发
人工智能·深度学习·mysql·vue·推荐算法·个性化音乐推荐系统·音乐预测
alexander06811 天前
使用vite脚手架,快速创建一个vue3的项目
vue
toooooop813 天前
UniApp Vue2 动态修改 SCSS 伪类颜色
vue
这是个栗子13 天前
微信小程序开发(九)- uni-app微信小程序商城
微信小程序·小程序·uni-app·vue·vuex
鹤鸣的日常14 天前
前端运行时动态环境变量方案
前端·react.js·docker·前端框架·vue·gitlab
来杯@Java14 天前
学生选课管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·maven·mybatis