空状态分无数据、无网络、无权限三种,需依请求生命周期精准判断:fetch异常→无网络;401/403→无权限;response正常但data为空→无数据;DOM结构须复用真实容器并动态挂载节点。

空状态不是一种状态,而是三种:无数据、无网络、无权限——每种背后的数据流、错误码、用户动作和文案策略都不同,混用会直接降低可信度。
怎么区分无数据 vs 无网络 vs 无权限?看 fetch 响应链
只检查 response.body 是否为空,90% 的空状态都会误判。真正要拆解的是整个请求生命周期:
-
fetch()抛出异常(如TypeError: Failed to fetch)→ 无网络(DNS 失败、离线、CORS 拒绝) -
response.ok === false且response.status是401或403→ 无权限 -
response.ok === true但data.length === 0(或data === null)→ 无数据(正常流程)
别在 .then() 里统一处理;必须在 catch() 和 if (!response.ok) 分支中各自响应。否则“暂无数据”按钮点了没反应,用户会以为功能坏了。
空状态容器必须复用真实内容的 DOM 结构和语义角色
比如真实列表是 <ul class="order-list"></ul>,那空状态就不能另起一个 <div class="empty">。否则:
立即学习“前端免费学习笔记(深入)”;
- 屏幕阅读器无法感知“这个区域本该是订单列表”,
role="status"失效 - Flex/Grid 布局中高度突变,页面抖动
- SSR 和 hydration 后类名不一致,触发强制重绘
正确做法是:空状态也用 <ul class="order-list" aria-live="polite" aria-label="订单列表为空"><li>暂无订单记录</li></ul>,仅通过子元素内容区分状态。图标、按钮等操作项作为 <li> 的子节点插入,保持层级一致。
三种空状态的文案与按钮必须严格对应用户控制权
用户看到提示后,下一步动作是否可控,决定了信任感:
-
无数据:用户能主动改变结果 → 文案强调筛选条件,按钮为“重置筛选”或“去添加”。例如:
暂无符合当前筛选的订单,可调整时间范围或状态重新查询 - 无网络:用户只能等待或重试 → 文案不归因于自身操作,按钮为“重新加载”,且需自动检测网络恢复后静默重试一次
- 无权限:用户无法自行突破 → 文案说明权限来源(如“请联系管理员开通订单查看权限”),按钮为“联系支持”或留空,避免误导点击
把 403 显示成“暂无数据”,再放个“去添加”按钮,等于告诉用户“你有权限但系统忘了给你”,这是最伤信任的设计。
JS 控制空状态节点挂载,别用 display: none 切换
用 el.style.display = 'none' 或 CSS 类切换,会在 SSR/hydration 阶段造成状态错乱,尤其当服务端已渲染空状态、客户端立刻拿到数据时,会闪一下再消失。
- 正确路径:用
document.createElement()或template.content.cloneNode(true)动态插入/移除整个空状态节点 - 空状态结构写在
<template id="empty-no-data">里,避免 XSS 和事件绑定丢失 - 移除时用
el.remove(),不是el.innerHTML = ''—— 后者会清空内部所有事件监听器和自定义属性
最难缠的问题往往不在“怎么显示”,而在“数据从空→有→空反复切换时,焦点是否保留在原位置、读屏播报是否准时、动画是否卡顿”——这些全取决于节点是否真正挂载/卸载,而不是显隐切换。



















