Pinia 中的僵尸状态:你可能已经遇到的静默 bug

发布: (2025年12月20日 GMT+8 06:02)
5 min read
原文: Dev.to

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 }
})
  1. 用户打开向导。
  2. 向导从第 2 步开始,并显示旧的邮箱。
  3. 验证意外失败。

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,那它就值得分享。

Back to Blog

相关文章

阅读更多 »