6.1 核心概念
- useStore 函数
defineStore返回的useXxxStore,在组件setup中调用即拿到 store 实例。- 响应式解构
- 直接解构会丢响应式,须用
storeToRefs把 state/getter 转成 ref。 - setup 外使用
- 在路由守卫、Axios 拦截器等非组件上下文用 store,需保证 Pinia 已激活或显式传入实例。
- getActivePinia
- Pinia 提供的获取当前活跃实例的 API,用于排查"store 在 pinia 激活前被调用"的错误。
- $subscribe
- 订阅 state 变化的实例方法,组件卸载时默认自动解绑(下章详解)。
- 组件外单例
- 同一 store 在应用中是单例,多个组件拿到同一份状态,天然共享。
6.2 在组件中获取 Store
<script setup lang="ts">
import { useCounterStore } from "@/stores/counter";
// setup 顶层调用,拿到响应式 store 实例
const counter = useCounterStore();
</script>
<template>
<p>{{ counter.count }}</p>
<button @click="counter.increment()">+1</button>
</template>
6.3 storeToRefs 保持响应式
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useUserStore } from "@/stores/user";
const store = useUserStore();
// ✅ state 与 getter 转成 ref,保留响应式
const { name, isAdult } = storeToRefs(store);
// ✅ action 直接从 store 解构(无需响应式)
const { setName } = store;
</script>
常见错误
const { name } = useUserStore() 会得到非响应式快照,store 改了组件不更新。务必对 state/getter 用 storeToRefs。
6.4 选项式 API 组件中使用
如果组件还在用 Options API,可用 Pinia 提供的映射辅助函数:
import { mapState, mapActions } from "pinia";
import { useCounterStore } from "@/stores/counter";
export default {
computed: {
// 映射 state 与 getter 到 this
...mapState(useCounterStore, ["count", "doubled"]),
},
methods: {
// 映射 action 到 this
...mapActions(useCounterStore, ["increment"]),
},
};
| 辅助函数 | 作用 | 放在 |
|---|---|---|
| mapState | 映射 state / getter(只读) | computed |
| mapWritableState | 映射可写 state | computed |
| mapActions | 映射 actions | methods |
| mapStores | 映射整个 store 为 xxxStore | computed |
6.5 在 setup 外部使用 Store
路由守卫、拦截器、工具函数不在组件 setup 内,此时调用 use 函数要注意 Pinia 是否已激活。
路由守卫中
import { useAuthStore } from "@/stores/auth";
router.beforeEach((to) => {
// 在守卫回调内部调用即可(此时 app 已挂载 pinia)
const auth = useAuthStore();
if (to.meta.requiresAuth && !auth.isLoggedIn) {
return { name: "login" };
}
});
非组件模块(如 API 层)中
若在 SPA 中确定 Pinia 已 app.use,直接调用 use 函数即可;SSR 或不确定时机时,需把 pinia 实例显式传入:
// main.ts 中先激活
import { createPinia } from "pinia";
const pinia = createPinia();
app.use(pinia);
// 其它模块显式传入 pinia,绕过"活跃实例"检测
const auth = useAuthStore(pinia);
“getActivePinia was called with no active Pinia”
这个报错意味着你在 app.use(pinia) 之前、或在模块顶层就调用了 use 函数。解决:把调用挪到函数内部(守卫/action 里),或显式传入 pinia 实例。
6.6 组件卸载与生命周期
- store 是应用级单例,组件卸载不会销毁 store,状态依然保留。
- 在组件里用
store.$subscribe/store.$onAction注册的监听,组件卸载时默认自动移除。 - 若希望监听在组件卸载后仍存活,传入
{ detached: true }选项。
<script setup lang="ts">
const cart = useCartStore();
// 组件卸载时自动取消订阅
cart.$subscribe((mutation, state) => {
console.log("cart 变化", state.items.length);
});
</script>
6.7 小结与下一步
- setup 中直接
useXxxStore();state/getter 解构用storeToRefs,action 直接解构。 - Options API 组件用
mapState/mapActions/mapStores。 - setup 外使用把 use 调用放进函数内部,或显式传 pinia 实例。
- 下一章讲 插件与持久化——扩展 Pinia、本地持久化、$subscribe/$onAction。