应通过 fetch 心跳校验服务可达性而非依赖 navigator.online,心跳需检查后端真实依赖、同源、带 abortcontroller 超时、间隔≥15秒;按钮禁用须同步 disabled 属性、css 样式与 form submit 拦截;网络恢复后需心跳成功才启用按钮,并防抖及持久化状态。

navigator.onLine 不足以触发按钮禁用
直接监听 navigator.onLine 并设 button.disabled = true 是常见错误。它在 WiFi 通但后端挂掉、DNS 失效、代理拦截时仍返回 true,按钮不会禁用;而飞行模式刚开启时又可能延迟 2–5 秒才触发 offline 事件,期间用户已点爆提交按钮。
真正要拦截的不是“有没有网卡”,而是“能不能拿到你的 API 响应”。必须用 fetch 心跳校验服务可达性,且心跳 endpoint 需由后端真实检查 DB 连通性、缓存状态等依赖项,不能只返回静态 200。
- 心跳 URL 必须是业务同源路径(如
/api/health),避免跨域预检干扰 - 用
AbortController控制超时,例如setTimeout(() => controller.abort(), 3000) - 心跳间隔 ≥15 秒——太频繁会触发限流、耗电、CORS 预检失败
按钮禁用需同步视觉 + 行为 + 可访问性
仅设 button.disabled = true 不够:若按钮是 <button type="submit"></button>,用户仍可用回车触发表单提交;若只加 pointer-events: none,键盘焦点和屏幕阅读器仍能操作它。
正确做法是三者并行:
- JavaScript 立即执行
btn.disabled = true(不是setAttribute('disabled', 'true')) - CSS 中定义
button:disabled规则,至少包含opacity: 0.5、cursor: not-allowed、background-color和color显式声明 - 对表单级按钮,必须同时监听
form.onsubmit,第一行就e.preventDefault()+submitButton.disabled = true
网络恢复后按钮不能立刻启用
收到 online 事件不等于服务已恢复。用户看到“已联网”提示后点按钮却失败,体验比一直禁用还差。
必须在 online 触发后,立即发一次心跳请求,仅当该次 fetch 成功返回 200 且响应体符合预期(如 { "status": "ok" }),才执行 btn.disabled = false 并更新 UI。
- 心跳失败时保持按钮禁用,并可叠加“重试中…”文案或加载动画
- 防抖处理:网络抖动会导致
online/offline频繁切换,建议加 1.5 秒延迟再执行状态变更 - 避免在心跳
.catch()里直接btn.disabled = false——这会掩盖真实失败
离线场景下按钮状态需持久化
用户断网刷新页面,按钮不该凭空恢复可用。前端内存变量失效后,状态丢失是常态。
解决方案取决于业务容忍度:
- 轻量级:用
localStorage记录上次心跳失败时间戳,页面加载时若距今 - 强一致性:服务端提供 /status 接口返回当前是否允许前端交互,前端首次加载即请求该接口决定初始状态
- 最稳妥:Service Worker 拦截所有导航请求,强制返回预缓存的
/offline.html,其中按钮默认禁用且无 JS 依赖
容易被忽略的是:禁用状态的生命周期必须和服务端实际可用性对齐,而不是和浏览器网络图标同步。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











