Loading · 加载中
用于有明确进度的异步操作(文件上传、分块下载),告诉用户「还需要多久」。
// 进度条
const bar = document.querySelector('.progress');
fetch(url).then(r => {
const total = +r.headers.get('Content-Length');
const reader = r.body.getReader();
let loaded = 0;
while(true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
bar.style.width = `${loaded/total*100}%`;
}
});
Spinner · 旋转等待
不知道要等多久的轻量操作(API 请求、路由跳转),给用户「正在处理」的反馈。
// 纯 CSS 旋转
.spinner {
width: 48px; height: 48px;
border: 4px solid #e5e7eb;
border-top-color: #6366f1;
border-radius: 50%;
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
Skeleton · 骨架屏
首次加载内容列表/卡片时,用灰色块占位以减少「白屏跳跃」,比 spinner 更具结构感。
等待数据返回
// 骨架 → 真实内容
<div class="skeleton">
<div class="sk-avatar"></div>
<div class="sk-line"></div>
</div>
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Empty · 空状态
数据为空或查询无结果时的友好提示,避免白屏让用户疑惑「是不是出 bug 了」。
// 列表为空
function render(items) {
if (items.length === 0) {
return <EmptyState
icon="📭"
title="暂无数据"
action=<Button onClick={create}>新建</Button>
/>
}
return <List items={items} />;
}
Error · 业务错误
可预期的业务异常(表单校验失败、权限不足、余额不足),用红色提示并给出解决路径。
!
操作失败
余额不足,无法完成此次充值。请先充值或选择其他支付方式。
// 错误边界 + 用户友好提示
try {
await api.pay(order);
} catch (e) {
if (e.code === 'INSUFFICIENT') {
showToast({ type: 'error', msg: e.message });
redirect('/wallet/recharge');
}
}
404 · 页面走丢
URL 不存在或资源已被删除时的兜底页,提供清晰的回家路径,避免用户「迷路」。
// Nuxt / Next 404 页面
<template>
<div class="not-found">
<h1>404</h1>
<p>{{ message }}</p>
<NuxtLink to="/">回家</NuxtLink>
</div>
</template>
export default {
props: ['message'],
}
500 · 服务异常
后端未捕获异常导致的 5xx 错误,给用户安抚文案 + 上报通道,避免把锅砸到用户脸上。
// Express 全局错误处理
app.use((err, req, res, next) => {
logger.error({ err, url: req.url });
reportToSentry(err);
res.status(500).render('500', {
requestId: req.id,
});
});
Offline · 断网
用户设备掉线或服务端不可达时的提示,附重试按钮让用户主动恢复,而非傻等。
// 监听网络状态
window.addEventListener('offline', () => {
showOfflineOverlay();
});
window.addEventListener('online', () => {
hideOfflineOverlay();
syncPendingQueue();
});
// navigator.onLine 仅作参考
console.log(navigator.onLine);
Maintenance · 维护中
计划内的停机维护,附倒计时让用户知道什么时候能恢复,避免反复刷新碰运气。
// 倒计时
const end = new Date('2026-08-21T03:00:00Z');
setInterval(() => {
const diff = end - new Date();
if (diff <= 0) return location.reload();
const h = String(diff/3600000| 0).padStart(2,'0');
const m = String(diff/60000 % 60| 0).padStart(2,'0');
const s = String(diff/1000 % 60| 0).padStart(2,'0');
document.getElementById('countdown')
.textContent = `${h}:${m}:${s}`;
}, 1000);