Pinia 实战:模块化架构设计、统一调度与持久化方案全解
本文从状态管理的设计原则出发,完整拆解 Pinia 在中大型项目中的工程化方案:目录结构、入口设计、模块化拆分、跨 store 调度、分级持久化、类型安全。附架构流程图与可直接落地的完整代码模板。
前言
很多团队上 Pinia 的姿势还停留在"每个页面写一个 store"的散兵游勇状态——模块拆分随意、持久化各写各的、跨模块调用混乱、类型声明缺失。项目一做大,状态管理反而成了新的技术债。
Pinia 架构要解决的核心问题:
- 模块化边界:按什么维度拆 store?拆多细?
- 统一入口:如何注册、如何导出、如何保证单例?
- 持久化策略:哪些存 localStorage?哪些存 sessionStorage?哪些只在内存?
- 跨模块调度:store 之间互相调用怎么写才不耦合?
- 类型安全:全链路 TS 类型推导怎么做?
- 与请求层联动:loading、error、重试这些通用逻辑怎么收敛?
本文给出一套可直接落地的中大型项目标准方案。
一、状态管理的核心设计原则
在动手写代码之前,先明确几条架构原则,这是所有设计的基石。
1.1 单一职责原则(SRP)
每个 store 只负责一个业务领域的状态,不混写。
- ✅
useUserStore:只管理用户信息、token、登录登出 - ✅
useAppStore:只管理全局 UI 状态(主题、侧边栏、语言) - ❌ 一个
useCommonStore里塞了用户、菜单、字典、配置——典型的上帝对象
1.2 领域驱动拆分(DDD 轻量化)
按业务领域拆,不按页面拆。页面是临时的,领域是稳定的。
| 领域模块 | 职责 | 生命周期 |
|---|---|---|
| user | 用户身份、权限、角色 | 全局持久化 |
| app | 主题、布局、语言、全局 loading | 全局持久化(UI偏好) |
| permission | 路由权限、按钮权限 | 内存 + 会话级 |
| route | 标签页、面包屑、缓存路由 | 会话级 |
| dict | 数据字典、枚举值 | 内存 + 按需缓存 |
| 业务模块(如 order / cart) | 各业务域状态 | 按需,部分持久化 |
1.3 Store 分层职责
每个 store 内部严格分三层,不混写:
state(数据层) → 只存数据,不写逻辑 getters(计算层) → 纯函数,派生状态,不修改数据 actions(行为层) → 唯一允许修改 state 的地方,承载业务逻辑虽然 Pinia 允许直接修改 state,但项目建议统一走 action 修改——不是技术限制,是工程规范。直接改的口子一开,三个月后你根本不知道 state 在哪被改的。
1.4 持久化分级原则
不是所有状态都要持久化。按数据特性分三级:
| 级别 | 存储介质 | 适用数据 | 示例 |
|---|---|---|---|
| L1 本地持久化 | localStorage | 跨会话保留的用户偏好 | 主题、语言、token |
| L2 会话级 | sessionStorage | 标签页内有效,关闭即清 | 标签页状态、临时表单 |
| L3 内存级 | 仅运行时 | 刷新即重置 | 权限列表、字典数据 |
