Pinia

在组件中使用 Store

掌握在 Vue 组件中读取 Pinia 状态、调用 actions、使用 getters 的正确方式,以及 storeToRefs 的使用场景。

🎯 引言

学会这篇文章,你能掌握在组件中使用 Pinia Store 的正确姿势。重点是:为什么直接解构会丢失响应式,以及 storeToRefs 到底解决了什么问题。


🧱 获取 Store 实例

<script setup> 中,调用对应的 use 函数即可:

App.vue
<script setup>
import { useCounterStore } from './stores/counter.js';

const counter = useCounterStore();
</script>
useCounterStore() 返回的是同一个实例。无论在多少个组件中调用,大家操作的都是同一份状态。

📖 读取 state 和 getters

拿到实例后,直接通过点语法访问即可:

App.vue
<template>
    <p>计数:{{ counter.count }}</p>
    <p>双倍:{{ counter.double }}</p>
</template>

<script setup>
import { useCounterStore } from './stores/counter.js';

const counter = useCounterStore();
</script>

🛠 调用 actions

action 的调用和普通方法一样:

App.vue
<template>
    <button @click="counter.add">加一</button>
    <button @click="counter.addN(5)">加五</button>
</template>

⚡ 为什么直接解构会丢失响应式

很多同学会这样写:

const counter = useCounterStore();
const { count, double } = counter; // ❌ 这样写会有问题

这样解构出来的 countdouble 只是当时的值。如果 Store 里的 count 后来变了,视图不会更新,因为 count 已经和 Store 断开联系了。

这和 Vue3 的 reactive 解构是一个道理:直接解构响应式对象会丢失响应式。

正确做法:storeToRefs

Pinia 提供了 storeToRefs,专门用来解构 Store 中的 state 和 getters,同时保持响应式:

App.vue
<script setup>
import { storeToRefs } from 'pinia';
import { useCounterStore } from './stores/counter.js';

const counter = useCounterStore();

// ✅ 保持响应式
const { count, double } = storeToRefs(counter);

// action 直接解构
const { add, addN } = counter;
</script>
storeToRefs 只对 state 和 getters 有效。actions 不是响应式数据,直接解构即可。

💡 对比三种写法

写法是否保持响应式适用场景
counter.count推荐,最稳妥
const { count } = counter不要这样写
const { count } = storeToRefs(counter)需要解构多个状态时
如果你不确定用哪种,直接用 counter.count 这种最原始的方式,永远不会出错。

🧾 小节总结

  • 在组件中调用 useXxxStore() 获取 Store 实例。
  • 通过 store.xxx 读取 state 和 getters,通过 store.action() 调用方法。
  • 直接解构 Store 会丢失响应式,要用 storeToRefs
  • storeToRefs 只处理 state 和 getters,actions 直接解构。

❓ 知识问答

Q1:模板中可以直接写 useCounterStore().count 吗?

可以,但每次都调用 use 函数不够优雅。推荐在 <script setup> 中赋值给一个变量,比如 const counter = useCounterStore()

Q2:storeToRefs 会把整个 Store 都转成 ref 吗?

不会。它只把 state 和 getters 转成 ref,actions 保持原样。

Q3:为什么 action 不需要 storeToRefs

因为 action 是方法,不是响应式数据。你调用它时只是触发一次函数执行,不需要追踪变化。

Q4:如果我只想解构一个 state,还需要 storeToRefs 吗?

需要。只要解构 state 或 getter,就要用 storeToRefs


🧪 小练习

下面的组件直接解构了 Store,导致响应式丢失。请改成正确写法:

Counter.vue
<template>
    <p>计数:{{ count }}</p>
    <p>双倍:{{ double }}</p>
    <button @click="add">加一</button>
</template>

<script setup>
import { useCounterStore } from '../stores/counter.js';

const { count, double, add } = useCounterStore(); // 请修改这一行
</script>

🎉 恭喜你掌握了在组件中正确使用 Store 的方法!下一篇我们专门讲 state 的修改方式。