人人都会AI编程

13.1 Pinia 核心体系

更新时间:2026-07-09

Pinia 是 Vue 官方推荐的状态管理库,也是 Vuex 5 的精神续作。如果你之前用过 Vuex,不妨把 Pinia 理解为“去掉所有繁琐设计、把 TypeScript 支持拉满、不再区分 mutation 和 action”的新一代方案。如果没用过状态管理,可以用一句话理解它:把组件间共享的数据和逻辑抽到独立的 Store 里,任何组件都能直接访问和修改,且保持响应式

定义一个 Store

Pinia 用 defineStore 创建一个 Store,每个 Store 就是一个独立的状态单元。它接收两个参数:唯一的 ID(名称)和一个配置对象,支持 Options Store(类似 Vue 组件的选项写法)和 Setup Store(组合式函数写法)。推荐使用 Setup Store,因为它更贴近 Composition API 的思维模式,也更利于类型推导。

// stores/useCounterStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  // State:用 ref 或 reactive 定义
  const count = ref(0)

  // Getters:用 computed 定义派生状态
  const doubleCount = computed(() => count.value * 2)

  // Actions:用普通函数定义操作
  function increment() {
    count.value++
  }

  function incrementAsync() {
    setTimeout(() => {
      count.value++
    }, 1000)
  }

  // 暴露给外部使用的成员
  return { count, doubleCount, increment, incrementAsync }
})

在组件中使用

Store 需要在 setup(或 <script setup>)中调用该 useXxxStore() 函数来实例化。一旦获取到 Store 实例,就能直接访问它的 State 和 Getters,甚至可以直接修改 State 而无需调用任何中间方法,这正是 Pinia 区别于 Vuex 的一大进步。

<template>
  <div>
    <p>计数:{{ counter.count }}</p>
    <p>两倍:{{ counter.doubleCount }}</p>
    <button @click="counter.increment()">+1</button>
    <button @click="counter.incrementAsync()">异步 +1</button>
    <!-- 甚至可以直接原地修改 -->
    <button @click="counter.count++">直接改</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '@/stores/useCounterStore'
const counter = useCounterStore()
</script>

这种“直接修改 State”的能力得益于 Pinia 内部使用 reactive 包裹了返回的对象,因此任何属性变更都能被响应式系统捕获并驱动视图更新。当然,对于包含业务校验、调用接口的复杂修改,还是建议封装成 Actions,让状态变更的意图更清晰。

State、Getters、Actions 三大核心概念

State
Store 的核心数据,相当于组件的 data,但它是全局可访问的。可以是基本类型、对象、数组,完全由你决定。修改它不需要 dispatch、commit,直接赋值即可,这是 Pinia 简化开发体验的关键设计。

Getters
派生状态,等价于计算属性。它依赖 State 或其他 Getters 计算得出,且带有缓存:只有依赖变化时才会重新计算。这对于避免重复运算、保持模板简洁非常有帮助。Getters 也支持传参(通过返回函数),例如:

const getTodoById = computed(() => {
  return (id) => todos.value.find(todo => todo.id === id)
})

Actions
处理业务逻辑的函数,支持同步和异步操作(如 API 请求)。与 Vuex 的最大区别是:没有 mutations,Actions 内部可以直接修改 State。这避免了在 Vuex 中“修改状态必须提交 mutation”的模板式代码,同时又能通过函数名直观地表达操作意图。Actions 里可以调用其他 Action,也可以用 this 访问 Store 实例(在 Setup Store 中直接用变量即可,无需 this),非常适合组织复杂的流程。

模块化设计:天然分离

Vuex 需要使用 Modules 并处理命名空间,而 Pinia 天生就是模块化的:一个 Store 就是一个独立的 JS/TS 模块。你可以创建多个 Store 文件,分别在需要的组件中导入。Store 之间也可以相互引用,例如用户 Store 可以使用订单 Store 里的方法,只需在 Action 内部调用另一个 Store 的 use 函数即可。

这种设计让大型项目的状态树自然拆解为“按业务域划分”的 Store,避免了单一状态树膨胀到难以维护的问题。它符合 Vue 3 组合式 API 的精神:按功能组合,而非按文件类型分离

对 Vuex 用户的核心优势

  • 没有 mutations:修改状态直接在 Action 或组件中进行,减少样板代码。
  • 更好的 TypeScript 支持:无需额外的类型包装,自动推导 State、Getter 的类型,开发体验丝滑。
  • 更简单的结构:不强制区分“模块内部的状态、getter、action”,所有逻辑在一个函数里清晰摆放。
  • DevTools 支持:Pinia 的调试工具完整追踪每个 Store 的状态变化、Action 调用和耗时,时间旅行调试依旧可用。

真实开发中的小提示

  • Store 的实例会在第一次调用 useXxxStore() 时创建并缓存,后续调用返回同一个实例。因此可以在不同组件间安全共享。
  • 如果将 Store 中的对象通过解构赋值取出,会丢失响应式(因为解构出的只是值快照)。需要保持响应式时,用 Pinia 提供的 storeToRefs() 工具函数。
  • 持久化插件(如 pinia-plugin-persistedstate)只需安装并简单配置,就能将指定 Store 的数据自动同步到 localStorage/sessionStorage,解决页面刷新状态丢失的常见痛点。

总的来说,Pinia 并不是一个“更复杂”的状态管理库,而是把状态管理变成了一个“组合式函数”的延伸。你自然地用 refcomputed 和普通函数写一个模块,然后把它共享出去——这就是 Pinia 的核心。