Chapter 06

组件中使用 Store

把 store 接进组件——正确解构保持响应式、在 setup 外部(路由守卫、工具函数)安全使用,以及理解生命周期。

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映射可写 statecomputed
mapActions映射 actionsmethods
mapStores映射整个 store 为 xxxStorecomputed

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 组件卸载与生命周期

<script setup lang="ts">
const cart = useCartStore();

// 组件卸载时自动取消订阅
cart.$subscribe((mutation, state) => {
  console.log("cart 变化", state.items.length);
});
</script>

6.7 小结与下一步