Pinia 高级状态管理技巧:从简单应用到复杂场景的最佳实践
Pinia 相比 Vuex 简洁了很多,但简洁不等于简单。很多团队用 Pinia 的方式仍然像用 Vuex:
- 把所有状态堆在一个大 store
- action 里逻辑混杂,很难测试
- 状态变化的链路追踪困难
本文讲清楚 Pinia 应该怎么用,避免"分散在三个 store 中而找不到逻辑源头"的困境。
1. 从理解 Store 的三层入手
每个 Pinia store 的三个部分,职责应该清晰分离:
State(数据层)
- 只存纯数据,不含计算
- 例如:userId、userList、searchQuery
Getter(计算层)
- 基于 state 派生的只读值
- 类似 computed property
- 例如:userById(id)、filteredUsers
Action(行为层)
- 修改 state 的唯一入口
- 包含同步和异步逻辑
- 例如:fetchUser、updateProfile
三者的边界要清晰,否则逻辑会四处蔓延。
2. 异步 action 的两种正确模式
模式 A:状态机风格
const store = defineStore('user', {
state: () => ({
user: null,
loading: false,
error: null,
status: 'idle' // 'loading' | 'success' | 'error'
}),
actions: {
async fetchUser(id) {
this.status = 'loading'
try {
this.user = await API.getUser(id)
this.status = 'success'
} catch (e) {
this.error = e
this.status = 'error'
}
}
}
})
优点:状态转换清晰可追踪,易于测试。
模式 B:Getter 推导风格
const store = defineStore('user', {
state: () => ({ user: null, pending: null, error: null }),
getters: {
isLoading: (state) => state.pending === 'fetch',
hasError: (state) => state.error !== null,
}
})
优点:UI 只关心"是否加载中",不需要管理额外 status 字段。
3. Store 如何拆分不会散乱
常见的错误是:
- 拆太多 store(10+ 个),跨 store 通信复杂
- 拆太少 store(1 个超级 store),维护噩梦
推荐的拆分策略按"业务域",而非"页面":
推荐做法
useAuthStore: 用户认证与权限useUserStore: 用户个人资料 & 重交互操作useListStore: 列表、筛选、排序状态useSettings: 应用全局设置
关键是:"一个 store 内的 state 通常会一起变化"。
4. Subscribe & Watch:状态变化监听的两条路
Subscribe(推荐用于副作用)
store.$subscribe((mutation, state) => {
console.log(`状态变化: ${mutation.type}`, state)
// 例如:同步到 localStorage
localStorage.setItem('userState', JSON.stringify(state))
})
用途:持久化、日志、分析事件。
Watch(推荐用于级联更新)
watch(
() => store.userId,
(newId) => {
store.fetchUserDetails(newId)
}
)
用途:级联查询、自动刷新依赖数据。
5. Devtools 集成与时间旅行调试
Pinia 内置了 Devtools 支持(自动),你可以:
- 看到每一次 action 触发
- 查看状态快照与变化
- 时间旅行(点击历史记录跳转状态)
这个功能强大到可以替代了很多手写日志。关键是养成"看 Devtools 找 bug"的习惯。
6. 常见失败案例
失败案例 A:Store 中的计算逻辑混乱
不要在 getter 中做网络请求或复杂文本处理,那应该在 action 中做。 Getter 只负责"数据形态转换"(例如 list.map、filter)。
失败案例 B:跨 store 的泥潭
userStore 的一个 action 依赖 listStore、settingsStore 和 authStore。 结果变成了大蜘蛛网,改一个 store 整个系统都可能崩。
解决方式:创建一个上层的 orchestration store,负责跨 store 协调。
7. 性能优化清单
- 给大列表的 state 做分页或虚拟滚动
- Getter 中避免创建新数组,用 computed property
- 用
storeToRefs而不是直接store.xxx来保留响应性 - 为关键 state 变化配置 subscribe 的 flush 策略(immediate/flush)
- 定期审计:哪些 state 从来没人用过,删掉它


