关于 pinia-v3
面向 Vue 和 Nuxt 开发者的 Pinia v3 技能指南,介绍 Vue 状态管理。内容涵盖 Store 模式、SSR、测试、HMR,以及从 Vuex 迁移的指导。
主要功能
- 01使用两种语法定义 Store
- 02管理状态、Getter 和 Action
- 03集成插件与组合式函数
- 04处理 SSR 和 Nuxt 状态水合
- 05测试 Store 和组件
- 06配置 HMR 并从 Vuex 迁移
适用场景
- 01组织 Vue 应用中的共享状态
- 02在 SSR 应用中水合 Store
- 03使用隔离状态测试 Store
- 04将 Vuex 模块迁移至 Pinia
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill pinia-v3安装后在 Claude Code 中输入/pinia-v3确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/example-template.txtreferences/example-reference.mdplugins-composables.mdssr-and-nuxt.mdstate-getters-actions.mdstore-syntax-guide.mdtesting-guide.mdvuex-migration-checklist.mdvuex-migration.mdscripts/api-store-example.tsauth-store-example.tsexample-script.shoption-store-template.tspersistence-plugin.tssetup-store-template.ts详细介绍
Pinia v3 - Vue 状态管理
状态:已可用于生产 ✅ 最近更新:2025-11-11 依赖项:Vue 3(或搭配 @vue/composition-api 的 Vue 2.7) 最新版本:pinia@^3.0.4、@pinia/nuxt@^0.11.2、@pinia/testing@^1.0.2
快速开始(5 分钟)
1. 安装 Pinia
bun add pinia
# or
bun add pinia
# or
bun add pinia
Vue <2.7 用户:还需使用 bun add @vue/composition-api 安装 @vue/composition-api
为什么这很重要:
- Pinia 是 Vue 官方的状态管理库
- 提供比 Vuex 更好的 TypeScript 支持
- 消除 mutations 和命名空间带来的复杂性
- 完整支持 DevTools,并提供时间旅行调试
2. 创建并注册 Pinia 实例
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
重要:
- 使用任何 store 之前,先安装 Pinia
- 挂载应用之前,调用
app.use(pinia) - 每个应用只能有一个 Pinia 实例(SSR 除外)
3. 定义你的第一个 Store
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Eduardo'
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
4. 在组件中使用 Store
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>
两种 Store 语法
加载 references/store-syntax-guide.md,查看 Option Store 与 Setup Store 的完整对比。
快速概览
Pinia 支持两种 Store 定义语法:
Option Store:
- 类似于 Vue Options API
- 内置
$reset()方法 - 适用于:较简单的使用场景,以及熟悉 Vuex 的团队
Setup Store:
- 使用 Composition API 模式
- 完整集成组合式函数
- 适用于:高级模式,以及需要集成侦听器/VueUse 的场景
→ 加载 references/store-syntax-guide.md,查看: 完整的语法对比、示例和选择标准
State、Getters 和 Actions
加载 references/state-getters-actions.md,查看完整的 API 参考。
快速参考
State:
- 在
state: () => ({...})(Option)或ref()(Setup)中定义 - 直接访问:
store.count - 直接修改:
store.count++或store.$patch({...}) - 重置:
store.$reset()(仅限 Option Store)
Getters:
- 计算属性:
getters: { double: (state) => state.count * 2 } - 使用
this访问其他 getter(必须为返回值指定类型)
Actions:
- 业务逻辑:
actions: { increment() { this.count++ } } - 可以是异步的
- 可以直接访问其他 Store
Store 解构:
import { storeToRefs } from 'pinia'
// ✅ For reactivity
const { name, count } = storeToRefs(store)
// ✅ Actions can destructure directly
const { increment } = store
→ 加载 references/state-getters-actions.md,查看: 完整 API、订阅、Store 组合模式、Options API 用法
插件和组合式函数
加载 references/plugins-composables.md,查看完整的插件和组合式函数指南。
插件基础
pinia.use(({ store, options }) => {
// Add properties to every store
return { customProperty: 'value' }
})
组合式函数集成
Option Store: 仅限在 state() 中使用 useLocalStorage 这类函数
Setup Store: 完整支持 VueUse/组合式函数
→ 加载 references/plugins-composables.md,查看: 完整的插件模式、VueUse 集成、TypeScript 类型、常见模式(持久化、路由器、日志记录器)
在组件外使用 Store
问题
Store 需要 Pinia 实例。该实例会自动注入组件,但在模块作用域中不可用。
❌ 错误:在模块级别访问 Store
// router.ts
import { useUserStore } from '@/stores/user'
// ❌ Fails: Pinia not installed yet
const userStore = useUserStore()
router.beforeEach((to) => {
if (userStore.isLoggedIn) { /* ... */ }
})
✅ 正确:在回调中访问 Store
// router.ts
import { useUserStore } from '@/stores/user'
router.beforeEach((to) => {
// ✅ Works: Called after Pinia is installed
const userStore = useUserStore()
if (userStore.isLoggedIn) { /* ... */ }
})
这样做可行的原因:路由器守卫会在 app.use(pinia) 执行完成后运行。
SSR:显式传入 Pinia 实例
// server-side
export function setupRouter(pinia) {
router.beforeEach((to) => {
const userStore = useUserStore(pinia) // Pass explicitly
})
}
服务端渲染与 Nuxt
加载 references/ssr-and-nuxt.md,查看完整的 SSR 和 Nuxt 集成指南。
SSR 快速参考
State 水合:
- 服务端:使用
devalue()序列化(不要使用JSON.stringify) - 客户端:在调用
useStore()之前完成水合 - 重要:在 actions 中的
await之前调用所有useStore()
Nuxt 3/4 集成
bunx nuxi@latest module add pinia
自动导入: defineStore、storeToRefs、usePinia、acceptHMRUpdate、所有 Store
→ 加载 references/ssr-and-nuxt.md,查看: 完整的 SSR 模式、Nuxt 配置、服务端数据获取、SSR 常见问题、调试
测试
加载 references/testing-guide.md,查看完整的测试指南。
测试快速开始
import { setActivePinia, createPinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia()) // Fresh Pinia for each test
})
组件测试
bun add -d @pinia/testing
import { createTestingPinia } from '@pinia/testing'
mount(Component, {
global: { plugins: [createTestingPinia()] }
})
→ 加载 references/testing-guide.md,了解: 完整的测试模式、操作存根、getter 模拟、异步测试、SSR 测试
热模块替换(HMR)
Vite 配置
// stores/counter.ts
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', {
// store definition
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
Webpack 配置
if (import.meta.webpackHot) {
import.meta.webpackHot.accept(acceptHMRUpdate(useCounterStore, import.meta.webpackHot))
}
优势:
- 编辑 store,无需完整重新加载页面
- 在开发期间保留应用状态
- 加快开发迭代
Options API 用法
对于仍在使用 Options API 的项目,请加载完整的 mapper 文档。
→ 加载 references/state-getters-actions.md,了解: 完整的 Options API 集成、所有 mapper(mapStores、mapState、mapWritableState、mapActions)
从 Vuex 迁移
加载 references/vuex-migration.md,查看完整的迁移指南。
快速转换概览
主要变更:
- 移除
namespaced(通过 store ID 自动实现) - 移除
mutations(直接修改状态) - 将
commit()替换为直接修改 - 将
rootState/rootGetters替换为 store 导入 - 使用
store.$reset()替代自定义的清除 mutation
目录: store/modules/ → stores/(每个模块 = 独立的 store)
→ 加载 references/vuex-migration.md,了解: 完整的转换步骤、组件迁移、检查清单、渐进式迁移策略
关键规则
务必执行
✅ 在 state() 中定义所有状态属性,或从 setup store 中返回这些属性
✅ 在组件中解构状态/getter 时使用 storeToRefs()
✅ 在挂载应用之前调用 app.use(pinia)
✅ 从 setup store 中返回所有状态(私有状态会破坏 SSR/DevTools)
✅ 在组件外使用时,在函数/回调中调用 useStore()
✅ 使用 acceptHMRUpdate() 支持开发时的 HMR
✅ getter 使用 this 访问其他 getter 时,注明返回值类型
✅ 使用 devalue 序列化 SSR 状态(防止 XSS)
✅ 在客户端调用任何 useStore() 之前,先 hydrate 状态(SSR)
✅ 在异步 action 中的任何 await 之前调用所有 useStore()(SSR)
切勿执行
❌ 在创建 store 后动态添加状态属性
❌ 不使用 storeToRefs() 就直接解构 store(会失去响应性)
❌ 将箭头函数用于 action(需要 this 上下文)
❌ 在 setup store 中返回私有状态(会破坏 SSR/DevTools/插件)
❌ 在模块顶层调用 useStore()(Pinia 安装前)
❌ 在 store 之间创建循环依赖(双方都读取对方的状态)
❌ 使用 JSON.stringify() 序列化 SSR 状态(容易受到 XSS 攻击)
❌ 在 action 的 await 之后调用 useStore()(会破坏 SSR)
❌ 使用 this 时忘记注明 getter 的返回值类型
❌ 在单元测试中省略 beforeEach(() => setActivePinia(createPinia()))
避免已知问题
此技能可避免 12 个有记录的问题:
问题 #1:直接解构导致响应性丢失
错误:解构后,模板中的状态变更不会更新
原因:JavaScript 解构会破坏 Vue 的响应性
预防:始终对状态/getter 使用 storeToRefs()
问题 #2:无法动态添加状态属性
错误:创建 store 后添加的新属性不具备响应性
原因:Pinia 需要预先定义所有属性以实现响应性
预防:在 state() 中声明所有属性,即使初始值为 undefined
问题 #3:Pinia 安装前找不到 Store
错误:getActivePinia() 返回 undefined
原因:在 app.use(pinia) 之前调用 useStore()
预防:在挂载应用或访问 store 之前调用 app.use(pinia)
问题 #4:Setup Store 的私有状态会破坏 SSR
错误:SSR 中的状态未能正确序列化/水合 原因:未从 setup 返回的属性不会被跟踪 预防:从 setup store 中返回所有状态属性
问题 #5:使用 this 的 Getter 无法推断类型
错误:Getter 使用 this 时,TypeScript 无法推断返回类型
来源:Pinia 已知的 TypeScript 限制
预防方法:显式指定返回值类型:getterName(): ReturnType { ... }
问题 #6:Options API Store 后缀混淆
错误:在组件中找不到 this.counterStore
原因:mapStores() 会自动添加 'Store' 后缀
预防方法:使用 Store 名称 + 'Store',或调用 setMapStoreSuffix()
问题 #7:await 后调用 Actions 会导致 SSR 出错
错误:SSR 使用了错误的 Pinia 实例,导致状态污染
原因:await 会改变异步函数中的执行上下文
预防方法:在任何 await 语句之前调用所有 useStore()
问题 #8:Store 循环依赖会导致应用崩溃
错误:超出最大调用堆栈 原因:两个 Store 在初始化期间读取彼此的状态 预防方法:通过 Getter/Action 访问其他 Store,而不是在 setup 阶段读取
问题 #9:SSR 状态序列化中的 XSS 漏洞
错误:状态中的用户输入可能会执行恶意脚本
原因:JSON.stringify() 不会转义可执行代码
预防方法:使用 devalue 库进行安全序列化
问题 #10:开发环境中的 HMR 不起作用
错误:修改 Store 后需要完整重新加载页面
原因:未为 Store 配置 Vite/webpack HMR
预防方法:在每个 Store 文件中添加 acceptHMRUpdate() 代码块
问题 #11:Composables 返回的函数会破坏 Option Store
错误:Store 状态包含无法序列化的函数
原因:Option Store 的 state() 只能返回可写的 refs
预防方法:复杂的 Composables 使用 setup Store,或仅提取可写状态
问题 #12:单元测试之间的状态未重置
错误:测试相互影响,偶尔失败
原因:多个测试共享同一个 Pinia 实例
预防方法:在测试套件中使用 beforeEach(() => setActivePinia(createPinia()))
包版本(已于 2026-08-03 验证)
核心: pinia@^3.0.4、vue@^3.5.24
Nuxt: @pinia/nuxt@^0.11.2、nuxt@^4.0.0(Nuxt 3 已于 2026-07-31 结束生命周期;v4 是当前稳定版本系列)
测试: @pinia/testing@^1.0.2、vitest@^2.0.0
SSR: devalue@^5.3.2(用于安全序列化)
常见模式
完整的模式示例请参阅参考文件:
- 身份验证 Store →
references/state-getters-actions.md - 持久化插件 →
references/plugins-composables.md - 表单 Store →
references/store-syntax-guide.md(setup Store 示例) - 路由器集成 →
references/state-getters-actions.md(在组件外访问 Store)
官方文档
- Pinia:https://pinia.vuejs.org/
- 入门指南:https://pinia.vuejs.org/getting-started.html
- 核心概念:https://pinia.vuejs.org/core-concepts/
- SSR 指南:https://pinia.vuejs.org/ssr/
- Nuxt 集成:https://pinia.vuejs.org/ssr/nuxt.html
- 测试:https://pinia.vuejs.org/cookbook/testing.html
- Vuex 迁移:https://pinia.vuejs.org/cookbook/migration-vuex.html
- GitHub:https://github.com/vuejs/pinia
故障排除
问题:"getActivePinia() was called with no active Pinia"
解决方案:
- 确保在挂载之前调用
app.use(pinia) - 如果在组件外部调用,请在回调/函数内部调用
useStore() - 对于 SSR,请显式传入 Pinia 实例:
useStore(pinia)
问题:状态更改不会更新模板
解决方案:使用 storeToRefs(),而不是直接解构
问题:使用 this 的 Getter 出现 TypeScript 错误
解决方案:显式指定返回值类型:myGetter(): ReturnType { return this.otherGetter }
问题:setup Store 中无法使用 $reset()
解决方案:手动实现自定义重置:
function $reset() {
count.value = 0
name.value = ''
}
return { count, name, $reset }
问题:Store 的 HMR 不起作用
解决方案:添加 HMR 接受代码块:
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}
问题:测试间歇性失败
解决方案:在 beforeEach() 中创建新的 Pinia:
beforeEach(() => {
setActivePinia(createPinia())
})
何时加载参考资料
在以下情况下加载 references/store-syntax-guide.md:
- 需要详细比较 Option 和 Setup store 语法
- 决定新 store 应使用哪种语法
- 有关 Option 和 Setup store 权衡的问题
- 需要两种语法的完整示例
在以下情况下加载 references/state-getters-actions.md:
- 需要 state、getter 或 action 的完整 API 参考
- 有关
$patch、$subscribe或$onAction的问题 - 实现 store 组合模式
- 使用 Options API 映射器(
mapStores、mapState、mapActions) - 在组件外访问 store(路由器、插件)
在以下情况下加载 references/plugins-composables.md:
- 创建自定义 Pinia 插件
- 将 VueUse 或其他组合式函数集成到 store 中
- 需要持久化、路由或日志插件模式
- 有关插件 TypeScript 类型的问题
- 高级组合式函数集成
在以下情况下加载 references/ssr-and-nuxt.md:
- 设置服务端渲染
- 与 Nuxt 3/4 集成
- 有关状态水合或序列化的问题
- SSR 相关错误(Pinia 实例错误、水合不匹配)
- Nuxt 自动导入或配置
- 服务端数据获取模式
在以下情况下加载 references/testing-guide.md:
- 为 store 设置单元测试
- 测试使用 Pinia store 的组件
- 需要 stub action 或模拟 getter
- 有关
createTestingPinia的问题 - 测试 SSR store
- Vitest 或测试框架集成
在以下情况下加载 references/vuex-migration.md:
- 将现有 Vuex 代码库迁移到 Pinia
- 有关 Vuex→Pinia 转换的问题
- 需要迁移清单或示例
- 需要渐进式迁移策略
完整设置清单
- 已安装
pinia包 - 已使用
createPinia()创建 Pinia 实例 - 已在挂载前通过
app.use(pinia)注册 - 已创建 store 目录(例如
src/stores/) - 已使用
defineStore()定义至少一个 store - 在组件中解构时已使用
storeToRefs() - 使用
this时已为 getter 返回值添加类型 - 已通过
acceptHMRUpdate()添加 HMR 支持(开发环境) - 已配置 SSR 水合(如果使用 SSR)
- 已配置
@pinia/nuxt(如果使用 Nuxt) - 已通过
createTestingPinia()设置测试(如果需要测试) - 所有 store 均遵循一致的命名方式:
use[Name]Store - 已验证 DevTools 集成正常工作
有疑问?遇到问题?
- 查看官方文档:https://pinia.vuejs.org/
- 查看上方的“已知问题预防”部分
- 确认已完成设置清单
- 检查 TypeScript 配置问题
- 确保在使用 store 前已安装 Pinia