Vibe Coding UI 状态矩阵:加载、空、错误与权限分支一次写清
只给 AI 一张成功态截图,它会自行猜测等待、空数据、失败和无权限行为。本文用状态矩阵、优先级与转换规则,让每个关键界面都有明确文案、动作和可测试出口。
本文要点
- 主题聚焦:AI、Vibe Coding、React
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
给编程 Agent 一句“做一个项目列表页”,它通常会返回有三张卡片的成功态。真实用户第一次打开时却没有项目,慢网下要等待,令牌过期后会收到 401,普通成员可能没有创建权限,后台失败后还可能保留旧数据。若这些状态不在规格里,AI 仍会实现,只是用它临时猜出的 spinner、空数组和通用 toast。
UI 状态矩阵不是把所有组件状态做笛卡尔积,而是从目标用户与主路径中的关键决策点出发,列出用户可能观察到且需要不同动作的状态。它与API 合同与请求响应示例共同定义前后端边界:接口返回什么是合同,界面怎样解释、恢复和保持上下文是矩阵。
先确定状态由什么事实决定
对每个主路径屏幕记录五个维度:请求生命周期 idle/loading/success/error;数据形状 none/some/stale/partial;身份 anonymous/authenticated/expired;授权 allowed/read-only/denied;连接 online/offline/reconnecting。只有会改变用户信息、可用动作或安全边界的组合才形成一行。
项目列表可先得到这张最小表:
| stateId | 前置事实 | 用户看到什么 | 可执行动作 | 禁止行为 |
|---|---|---|---|---|
LIST_LOADING_INITIAL | 首次请求未完成 | 保持标题和稳定骨架,标记正在加载 | 取消导航 | 显示“暂无项目” |
LIST_EMPTY_CAN_CREATE | 请求成功、0 项、有创建权 | 空状态说明及创建按钮 | 创建、查看导入说明 | 自动造示例项目 |
LIST_EMPTY_READ_ONLY | 请求成功、0 项、只读 | 无项目且说明权限来源 | 返回或联系管理员 | 显示不可用的创建表单 |
LIST_REFRESH_FAILED | 已有旧数据、刷新失败 | 保留旧数据,标注更新时间与失败 | 重试、继续只读浏览 | 清空成空状态 |
LIST_AUTH_EXPIRED | 服务端确认会话失效 | 中断写操作并说明需重新登录 | 登录后回到原位置 | 把 401 当普通网络错误重试 |
空状态只能由一次成功响应证明。items=[]、字段缺失、请求未结束和解析失败不是同一件事。错误也要按用户可采取的动作分类:可立即重试、需修改输入、需重新认证、无权限、服务暂不可用、未知失败。不要把内部异常原文直接展示,也不要用“Something went wrong”抹掉恢复路径。
为状态规定优先级和转换
同一时刻可能同时满足多个条件:加载刷新时已有旧数据,随后会话又过期。规格必须声明优先级。安全边界通常高于便利:确定的 denied 或 expired 先移除受限操作;已有数据是否保留要依据其敏感级别决定。初次加载高于空状态;后台刷新不应覆盖已有内容;离线状态不应把服务端 403 改写成“网络问题”。
用显式转换表写 from + event -> to + sideEffect。例如 LIST_READY + REFRESH_STARTED -> LIST_REFRESHING 保留当前行;LIST_REFRESHING + REQUEST_FAILED -> LIST_REFRESH_FAILED 保存最后成功时间;EDITING + AUTH_EXPIRED -> LIST_AUTH_EXPIRED 把未提交草稿留在本地受控区域,重新认证成功后询问是否恢复。没有定义的事件应记录并停在安全状态,不能静默跳到 success。
竞态是 AI 生成界面的常见漏项。用户快速切换筛选时,旧请求可能晚于新请求返回。为每次加载绑定 request generation,只有当前 generation 能提交可见状态;卸载后结果不得更新组件。重复点击提交使用禁用、幂等键或两者结合,具体服务端语义由数据模型与业务不变量约束。
权限状态不能只靠隐藏按钮
前端隐藏按钮只改善体验,不提供授权。矩阵中的 allowed/read-only/denied 必须来自可信权限合同,并在服务端对每次操作复核。404 用于隐藏资源存在性还是 403 明示拒绝,要在 API 规格中统一,不能让各页面自行猜测。
考虑权限在页面打开后被撤销:用户仍看到旧内容,点击保存时服务端拒绝。界面应停止重试,保留可安全导出的草稿或明确丢弃策略,刷新权限事实,并解释下一步。若删除或支付等高风险动作权限不确定,默认不执行;Skeleton 不应短暂显示受限按钮造成闪烁。
给等待和变化提供可感知反馈
视觉 spinner 不等于状态可访问。初始加载保留结构和可理解文本;超过预定等待阈值后说明仍在处理,并在可取消时提供取消。操作完成、结果数变化或保存失败等不移动焦点的动态更新,按 WCAG 状态消息要求让辅助技术获知,同时避免抢夺焦点。
ARIA alert 适合需要立即注意的动态错误,不应在页面初次加载时滥用,也不应把整页普通内容包进 alert。错误与对应字段建立程序化关联;焦点移动到第一个错误还是留在提交按钮,按任务恢复成本预先决定。键盘、屏幕阅读器和 200% 放大下的完成能力进入非功能预算,不是矩阵之外的“美化”。
从矩阵生成测试,而不是生成实现事实
每一行至少有一个渲染测试,验证关键文案、动作是否存在及禁止动作是否缺席;每条高风险转换有交互或端到端测试。fixture 直接使用合同化的 API 响应,不能让前端测试与 Mock 各自发明错误形状。再加入乱序响应、重复提交、权限撤销、离线恢复和慢请求的时序测试。
矩阵中每行标记 implemented/tested/reviewed,只有三项均有当前证据才能进入 Definition of Done 与交付边界。Agent 新增组件状态时必须更新矩阵或说明它只是内部实现态;规格变化则进入规格变更日志与重新验收。
状态矩阵的价值不是更多文档,而是取消默认猜测。它让设计、前端、后端和测试用同一组 stateId 讨论:什么事实发生、用户知道什么、能做什么、系统绝不能做什么。成功态只是一行;真正可信的 Vibe Coding 界面,来自其余行也有明确而诚实的出口。
Related