大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Pinia 状态管理
这一课你将能做到
- 用组合式写法定义 Pinia store,在组件中读写状态与 actions
- 用
storeToRefs解构保持响应式 - 对照 Redux/React Context:何时需要全局 store,何时组件内 state 足够
为什么要学
跨页、跨组件共享的数据(当前用户、购物车、Todo 列表)若层层 props 会很烦。
Pinia 是 Vue 3 官方推荐的状态库,API 比早期 Vuex 轻。本站 React 侧常用局部 state + Context;学 Pinia 是为了读懂 Vue 中后台项目。
先建立图像
组件 A ──┐
组件 B ──┼─ useTodoStore() ──> 同一份 state
路由页 ──┘
| 概念 | 人话 |
|---|---|
| state | 全局记得的数据 |
| getters | 类似 computed |
| actions | 改状态/异步的方法 |
跟着做
步骤 1:安装与注册
npm install pinia
import { createPinia } from "pinia";
app.use(createPinia());
create-vue 勾选则已就绪。
步骤 2:定义 store
// stores/todo.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";
export const useTodoStore = defineStore("todo", () => {
const items = ref([]);
const filter = ref("all");
const visible = computed(() => {
if (filter.value === "active") return items.value.filter((t) => !t.done);
if (filter.value === "done") return items.value.filter((t) => t.done);
return items.value;
});
function add(text) {
const t = text.trim();
if (!t) return;
items.value.push({ id: Date.now(), text: t, done: false });
}
function toggle(id) {
const row = items.value.find((x) => x.id === id);
if (row) row.done = !row.done;
}
return { items, filter, visible, add, toggle };
});
这是 setup store 风格,与组件组合式一致。
步骤 3:组件使用
<script setup>
import { storeToRefs } from "pinia";
import { useTodoStore } from "../stores/todo";
const store = useTodoStore();
const { visible, filter } = storeToRefs(store);
</script>
<template>
<input
@keyup.enter="
store.add($event.target.value);
$event.target.value = '';
"
/>
<button
v-for="f in ['all', 'active', 'done']"
:key="f"
type="button"
@click="filter = f"
>
{{ f }}
</button>
<ul>
<li v-for="t in visible" :key="t.id">
<label>
<input type="checkbox" :checked="t.done" @change="store.toggle(t.id)" />
{{ t.text }}
</label>
</li>
</ul>
</template>
步骤 4:解构陷阱
const { items } = store; // 丢响应式
const { items } = storeToRefs(store); // 正确
store.add; // 方法可直接解构,不必 toRefs
步骤 5:持久化小练习
在 store 里 watch(items, …, { deep: true }) 写入 localStorage,启动时读回。
刷新后列表应还在——综合练习会用到。
步骤 6:对照 React
| 需求 | Vue | React(本站常见) |
|---|---|---|
| 全局客户端状态 | Pinia | Context / 轻量库 |
| 服务端真相 | 仍是 API | 仍是 Spring Boot |
| 别把一切塞全局 | 能本地就本地 | 同 |
讲清楚要点
- Pinia 不是数据库:刷新默认清空,除非你持久化。
- 按领域拆 store:
user/todo,避免上帝 store。 - 异步放 actions:先改 loading,再请求,再写数据。
- DevTools 可时间旅行调试,利于排错。
- SSR 需小心单例:入门 SPA 无妨。
- 与 Router 配合:守卫里读
useUserStore()查登录态。
你可能会懵的地方
-
Vuex 还学吗?
维护旧项目需要;新项目 Pinia。 -
options store 写法?
state/getters/actions对象形式仍支持;本课组合式优先。 -
能直接改 state 吗?
Pinia 允许;团队也可约定只走 actions,便于追踪。 -
和 composable 重复?
composable 可封装局部复用;跨树共享真相用 store。 -
Redux 样板多?
对照时别被吓到;Pinia 更轻,思想都是「单一可预测状态」。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 解构 store 丢响应 | UI 不更新 | storeToRefs |
| 所有 UI 状态塞 Pinia | 难维护 | 局部 ref 优先 |
| 在组件散落改深层结构无约定 | 难审计 | 集中 actions |
| 把密钥放 store | 不安全 | 密钥留服务端 |
| 多个 store 循环依赖 | 初始化痛 | 理清依赖或合并 |
| 当服务器数据缓存唯一方案 | 过期难 | 明确失效策略 |
自测三问
- 何时该用 Pinia 而不是组件内 ref?
- 为什么
storeToRefs必要? - Pinia 与后端 API 各自负责什么「真相」?
小结一句话
Pinia 用 store 共享客户端状态;组合式定义 + storeToRefs 使用,并与 React 全局状态方案对照理解。
官方延伸
- Pinia 文档
- 下一课:数据获取