第五章: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 全栈开发实战专栏

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

相关推荐
Sheldon一蓑烟雨任平生13 小时前
Vite 深度剖析(一)
vue·react·vite·环境变量·esbuild·vite.config.ts·依赖预构建
zhz521414 小时前
一个简单、轻量级且安全的离线GIS 系统架构设计
安全·系统架构·vue·gis·fastapi
旷世奇才李先生1 天前
Vue3\+Pinia实战:企业级后台管理系统开发(附权限控制)
vue
空中海2 天前
Vue 全栈开发知识体系
vue
吴声子夜歌3 天前
Vue3——Vuex状态管理
前端·vue.js·vue·es6
sp42a4 天前
NativeScript ListView 实现固定分区标题
vue·nativescript
双普拉斯4 天前
打造工业级全栈文件管理器:深度解析上传、回收站与三重下载流控技术
spring·vue·js
码界筑梦坊4 天前
356-基于Python的网易新闻数据分析系统
python·mysql·信息可视化·数据分析·django·vue·毕业设计
吴声子夜歌5 天前
Vue3——渲染函数
前端·vue.js·vue·es6