四态齐全
空、加载、出错、正常。AI 默认只写最后一种。
空、加载、出错、正常。一块界面在真实环境里会出现这四种样子,AI 通常只做出其中一种。剩下三种要么一片空白,要么直接把报错摊在用户面前。
你会遇到的现象:
- 自己演示一切正常,同事一登录就是白屏,因为他账号里没数据
- 网慢的时候页面先塌下去,数据回来又撑开,正要点的按钮跑掉了
- 接口一挂整页变成 Uncaught TypeError,用户只能刷新碰运气
原因不在模型。你说做一个订单列表,它脑子里的画面就是一屏排列整齐的订单,因为你给它的描述里也只有这一种。空、加载、出错这三种情况在需求文档里从来不出现,只在真实用户手上出现:新注册的人第一次打开、地铁里信号断了、后端刚好在发版。
补齐的成本通常是几十行代码。真正的门槛是在写需求的时候就把它们当成需求的一部分,而不是等 bug 报上来再补。
怎么判断
任何一次取数据,结果只会落在四个格子里。照着这张图问一遍,就知道少写了哪个:有数据是正常态,数组为空是空态,正在请求是加载态,请求失败是错误态。
空态还要再分一层:是这个人本来就没有数据,还是筛选条件把数据筛没了。前者引导他创建,后者提示放宽条件并给一键清除。
设计考量
- **空态是介绍产品的机会,别只写暂无数据。**用户第一次进来看到的多半就是空态。这块地方应该说清楚这里将来会有什么、以及怎么让它有,一句解释加一个主按钮。四个字的「暂无数据」把最好的教学位置浪费掉了。
- **加载用骨架屏,别用居中转圈。**骨架屏提前占住内容将来的位置,数据到达时页面不跳动;转圈会让整块区域先塌再撑开。超过一秒的等待才需要显示加载态,更短的反而闪一下更难受。加载与进度指示的做法可以参看 Material Design 的规范。progress-md
- **错误文案按「说明当前状况 + 引导措施」写。**先讲发生了什么(没连上服务器),再讲他能做什么(重试或检查网络)。这个公式套在任何一条提示上都成立。重试按钮放在文案旁边,别让人去刷新整页。
- **提示用什么形式,取决于这件事严不严重。**三档从强到弱:必须立刻处理用弹窗,状态变化用全局横幅,可看可不看用气泡。越级使用的代价是用户很快学会闭眼点确定。
- **局部失败就局部提示,别让整页塌掉。**侧边栏的推荐模块挂了,不该导致整个页面变成错误页。状态的粒度要跟数据源的粒度对齐,每个独立请求各自管好自己那块区域。
- **四种状态的容器高度尽量接近。**切换时高度剧烈变化会导致页面跳动,用户正要点的按钮会跑掉。给容器一个 min-height,是成本最低的体验改善。
- **错误文案里不要用感叹号,句尾也不加标点。**感叹号会把一次网络波动渲染成事故。等待类文案统一用省略号收尾(加载中…),这是中文界面里比较通行的写法。
给 AI 的话
放进组件需求,或者直接沉淀到项目的 CLAUDE.md,之后每次生成数据组件都会默认带上四态:
凡是涉及异步数据的组件,必须同时实现四种状态,缺一不可:
1. 正常态:有数据时的正常渲染。
2. 空态:数据为空数组时。区分两种来源——
- 用户还没有任何数据:给一句说明 + 一个主行动按钮
- 筛选/搜索后无结果:提示放宽条件 + 一键清除筛选
3. 加载态:使用骨架屏,形状和数量贴近真实内容,不要用居中 spinner。
4. 错误态:按「说明当前状况 + 引导措施」写文案,配一个「重试」按钮,
不要暴露状态码或堆栈。
文案规范:
- 不使用感叹号;句尾除疑问句外不加标点。
- 等待类文案统一以「…」结尾,例如「加载中…」。
- 提示强度分三档:必须立刻处理用弹窗,状态变化用全局横幅,
可看可不看用气泡。不要越级。
其他约束:
- 四种状态复用同一个外层容器,设置 min-height 避免切换时页面跳动。
- 错误只影响当前组件,不要向上冒泡导致整页变成错误页。
- 把状态做成组件的一个 prop('ok' | 'empty' | 'loading' | 'error'),
这样我可以在不改后端的情况下逐个预览。
先告诉我这四种状态分别打算怎么呈现,我确认后你再写代码。
最后一句很关键。让它先描述方案,你能在几秒内看出空态是不是又写成了四个字的「暂无数据」,比读完代码再返工快得多。
真实案例
- Notion 的空数据库会给出几个模板选项和一个创建入口,用这块地方教用户接下来做什么。
- Linear 的加载骨架复刻了真实内容的行高和分栏,数据到达时几乎察觉不到切换,页面不发生跳动。
