计算属性名不直接重构状态机,但能显著提升其可维护性——通过集中声明状态/事件键、支持类型提示与自动补全,避免硬编码字符串导致的错配和拼写错误,实现零运行时字符串依赖。

计算属性名本身不直接重构状态机,但它能显著简化状态机配置的可维护性——尤其在 legacy 系统中替换散落的字符串字面量、避免硬编码状态/事件名引发的错配和 typo。
用计算属性名统一管理状态与事件键
遗留系统常出现类似 if (state === 'loading_user') {...} 或 dispatch('FETCH_USER_SUCCESS') 这类硬编码字符串。一旦拼写不一致或状态增删,极易引发静默失败。改用计算属性名可集中声明、类型提示、自动补全:
- 定义一个常量对象,所有状态名、事件名均通过
Object.keys()或as const推导类型(TypeScript) - 例如:
const States = { IDLE: 'idle', SCANNING: 'scanning', CONNECTED: 'connected' } as const - 后续所有状态判断、转换配置、UI 条件渲染都引用
States.IDLE,而非字面量'idle' - 状态机 transition 配置中,
from: States.IDLE和to: States.SCANNING可被 IDE 校验,重命名时一键更新全部引用
配合状态机库实现“零运行时字符串”配置
以 javascript-state-machine 或 XState 为例,传统写法易因字符串错位导致非法跳转;使用计算属性名后,配置完全脱离字符串依赖:
- 定义
const Events = { START_SCAN: 'START_SCAN', CANCEL: 'CANCEL', CONNECTED: 'CONNECTED' } as const - 状态机 transitions 数组变为:
{ name: Events.START_SCAN, from: States.IDLE, to: States.SCANNING } - 组件中触发事件也统一为
service.send({ type: Events.START_SCAN }) - 这样即使未来将
START_SCAN改为INITIATE_SCAN,TS 编译器立刻报错未使用的旧名,且所有引用同步更新
逐步迁移:从字符串字面量到符号化键的三步走
面对无类型检查的 JS legacy 项目,不必一次性重写整个状态机,可渐进落地:
- 第一步:在全局 scope 新建
STATE_KEYS和EVENT_KEYS对象,仅作为字典使用(不修改原逻辑) - 第二步:将关键分支(如 API 请求生命周期)中的字符串替换成
STATE_KEYS.LOADING,并用console.assert校验值存在,防止误用 - 第三步:将原 if/else 状态判断封装为函数,如
isInState(state, STATE_KEYS.CONNECTED),为后续接入正式状态机铺路
规避常见陷阱:命名一致性与作用域污染
计算属性名不是万能胶,需注意两个实际约束:
- 避免在不同模块重复定义同名常量对象(如两个
STATES),应提取到 shared/utils/state-keys.ts 中统一导出 - 禁止用动态拼接生成状态名(如
`${prefix}_loading`),这会破坏类型推导和静态分析能力 - 若 legacy 系统使用字符串模板(如 Vue 模板中
v-if="state === 'error'"),需配合编译时插件或 ESLint 规则(如no-string-literals)引导替换

















