Store 组合、TypeScript 与插件
🎯 引言
学会这篇文章,你能知道中大型项目里应该怎么拆分和组织多个 Store,怎么给 Pinia 加上 TypeScript 类型,以及怎么用插件做数据持久化。我们会避开前面文章已经讲过的内容,只聚焦这几个进阶话题。
🧱 多个 Store 如何组织
项目变大后,把所有状态塞进一个 Store 会很乱。推荐按功能拆分:
stores/
├── user.js
├── cart.js
└── settings.js
比如:
useUserStore:登录状态、用户信息useCartStore:购物车商品useSettingsStore:主题、语言等配置
每个 Store 只关心自己的状态,互相独立。需要组合时再通过引入实现。
⚡ Store 之间如何组合
当某个业务需要多个 Store 的数据时,可以在 action 中引入其他 Store:
import { defineStore } from 'pinia';
import { useCartStore } from './cart.js';
export const useOrderStore = defineStore('order', {
actions: {
async submit() {
const cart = useCartStore();
await apiCreateOrder({ items: cart.items });
cart.clear();
},
},
});
🛠 Pinia 与 TypeScript
Pinia 对 TypeScript 很友好。给 state 加上类型断言后,类型推断会自动生效。
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
message: '' as string,
}),
getters: {
double(): number {
return this.count * 2;
},
},
actions: {
add(n: number) {
this.count += n;
},
},
});
组件中使用:
<script setup lang="ts">
import { useCounterStore } from './stores/counter.ts';
const counter = useCounterStore();
counter.add(5); // TS 会提示参数必须是 number
</script>
as Type 做类型断言即可,比如 items: [] as Product[]。🔌 插件:扩展 Pinia
Pinia 支持插件,可以在每个 Store 创建时注入额外能力。常见的用途有:日志、持久化、调试等。
注册插件的方式:
import { createPinia } from 'pinia';
const pinia = createPinia();
pinia.use(({ store }) => {
console.log('Store 创建:', store.$id);
});
app.use(pinia);
store 参数就是当前 Store 实例。💾 数据持久化思路
最常见的插件需求是把状态保存到 localStorage,刷新页面后不丢失。核心思路只有两步:
- Store 创建时,从
localStorage读取之前保存的状态。 - 状态变化时,把新状态写回
localStorage。
export function piniaPersistPlugin({ store }) {
const key = `pinia-${store.$id}`;
const saved = localStorage.getItem(key);
if (saved) {
store.$patch(JSON.parse(saved));
}
store.$subscribe((mutation, state) => {
localStorage.setItem(key, JSON.stringify(state));
});
}
注册:
import { piniaPersistPlugin } from './plugins/persist.js';
pinia.use(piniaPersistPlugin);
pinia-plugin-persistedstate,它支持选择持久化字段、自定义存储方式等,更健壮。🧾 小节总结
- 中大型项目按功能拆分 Store,如 user、cart、settings。
- Store 之间可以在 action 中相互引入,但要避免循环依赖。
- Pinia 原生支持 TypeScript,用类型断言可以让类型推断更准确。
- 插件可以扩展 Pinia 能力,最常见的应用是数据持久化。
❓ 知识问答
Q1:Pinia 还需要像 Vuex 那样配置 modules 吗?
不需要。每个 Store 本身就是一个独立模块,按功能拆分即可。
Q2:两个 Store 互相引用会怎样?
可能导致循环依赖,程序报错。建议把公共状态抽到第三个 Store 中。
Q3:$subscribe 是什么?
它是 Store 实例上的方法,用来监听 state 变化,适合在插件中做持久化、日志等。
Q4:所有状态都适合持久化吗?
不是。敏感信息、临时状态、大量列表数据不建议持久化。只持久化真正需要跨页面保持的数据。
🧪 小练习
补全下面的 useSettingsStore,用 TypeScript 实现主题切换:
theme类型为'light' | 'dark',初始为'light'。toggleTheme()action 在两种主题之间切换。
import { defineStore } from 'pinia';
type Theme = 'light' | 'dark';
export const useSettingsStore = defineStore('settings', {
state: () => ({
// 请在这里定义 theme
}),
actions: {
toggleTheme() {
// 请在这里编写代码
},
},
});
🎉 恭喜你已经完成了 Pinia 课程的学习!从基础安装到 Store 组合、TypeScript 和插件,你已经可以在 Vue3 项目中熟练使用 Pinia 了。
