Pinia 中的僵尸状态:你可能已经遇到的静默 bug
Source: Dev.to
什么是 Zombie State(僵尸状态)?
- 页面、用户流程、已完成的表单或已结束的异步请求结束后仍然残留的状态。
- 它仍然可以:
- 预填输入框
- 触发验证
- 影响 UI 决策
- 造成“随机”错误
因为 Pinia store 默认是 全局、长生命周期的单例,离开页面并不会 重置 store 数据。除非你显式地重置它,否则状态会一直保存在内存中,即使已经不再有意义。
一个非常常见的例子
// useWizardStore.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useWizardStore = defineStore('wizard', () => {
const step = ref(2)
const email = ref('old@email.com')
return { step, email }
})
- 用户打开向导。
- 向导从第 2 步开始,并显示旧的邮箱。
- 验证意外失败。
store 从未被重置,产生了僵尸状态。
与异步相关的僵尸状态
async function loadUser() {
user.value = await fetchUser()
}
如果异步响应在组件已经卸载或上下文已经改变之后才到达,陈旧的数据会覆盖当前状态,导致同样的问题。
需要留意的症状
- 表单已经被填充但不应该出现
- 首次渲染时出现验证错误
- 导航(后退/前进)后出现错误数据
- UI 在导航后表现异常
- 刷新页面后错误消失
这些 bug 难以调试,因为它们依赖于导航历史而不是当前状态。
防止僵尸状态
1. 添加显式重置
// useWizardStore.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useWizardStore = defineStore('wizard', () => {
const step = ref(1)
const email = ref('')
function reset() {
step.value = 1
email.value = ''
}
return { step, email, reset }
})
import { onUnmounted } from 'vue'
import { useWizardStore } from '@/stores/useWizardStore'
const store = useWizardStore()
onUnmounted(() => store.reset())
Pinia 不会自动重置状态——你必须有意识地去做。
2. 在上下文变化时重置
import { watch } from 'vue'
import { useRoute } from 'vue-router'
import { useWizardStore } from '@/stores/useWizardStore'
const route = useRoute()
const store = useWizardStore()
watch(
() => route.params.id,
() => store.reset()
)
这样可以防止旧数据泄漏到新上下文中。
3. 保护异步请求
let requestId = 0
async function load() {
const id = ++requestId
const data = await fetchData()
if (id !== requestId) return // 忽略陈旧响应
state.value = data
}
延迟的响应会被忽略,而不是覆盖有效状态。
4. 将 UI 状态与业务状态分离
- 适合放入 store 的对象: 用户数据、认证信息、共享的业务实体。
- 不适合放入 store 的对象: 短生命周期的 UI 状态(例如弹窗可见性、临时表单字段)。这些应当靠近组件本身。
陈旧状态的类型
| 类型 | 含义 |
|---|---|
| Stale state | 仍然有效的旧状态 |
| Zombie state | 已失效但仍然活跃的状态 |
结论
僵尸状态危险在于它 看起来 正常,直到出现问题。当状态的生命周期超过其拥有者时,就会变成僵尸。这个问题并非 Pinia 独有,而是 状态生命周期问题。
Pinia 为我们提供了强大的工具,但随之而来的是责任:
- 明确定义所有权
- 控制生命周期
- 有意识地进行重置
遵循这些实践可以让应用:
- 更易于推理
- 更易于调试
- 对用户更可预测
如果这篇文章能帮助哪怕一位开发者避免一次难以追踪的线上 bug,那它就值得分享。