表单提交失败后必须用 event.preventdefault() 拦截默认行为,再封装可重试的异步逻辑;fetch 失败需手动检查 response.ok 并仅对 typeerror、aborterror 和 5xx 递归重试,配合幂等性校验与禁用按钮防重复。

表单提交失败后不能靠刷新页面或重点按钮来“重试”,必须用 event.preventDefault() 拦截默认行为,再封装可重复调用的异步提交逻辑——否则所有 JS 控制都会中断,用户看到的只是页面闪一下就没了。
submit 事件里必须 first 行就 preventDefault()
原生表单提交会立刻跳转或刷新,JS 逻辑(fetch、localStorage、错误捕获)全部失效。常见错误是只监听 submit 但没阻止默认行为,或者把 preventDefault() 写在验证之后——验证失败时容易漏掉。
- 移动端尤其危险:键盘弹起/收起、屏幕旋转都可能意外触发 submit,必须第一时间锁住流程
-
button.disabled = true只是辅助,核心防线永远是event.preventDefault() - 别用内联
onsubmit="return false",JS 表达式返回0或空字符串不是严格false,不可靠
fetch 失败重试前必须手动判断 response.ok
fetch 对 HTTP 404/500 不抛错,只把 response.ok 设为 false;不检查这一步,重试函数根本收不到失败信号,DevTools 看到红标但 JS 无反应,八成卡在这里。
- 每次
await fetch(url)后立刻写:if (!response.ok) throw new Error(`HTTP ${response.status}`) - 别写
fetch(url).then(r => r.json()):404 返回 HTML 页面时会抛TypeError: Response body is not JSON,原始状态码和原因全丢 - 仅对
TypeError(断网、CORS)、AbortError(超时)和 HTTP 5xx 重试;4xx(如 400、401、429)是客户端问题,重试无意义
重试必须递归 + 指数退避 + 硬限制次数
用 while 或 setInterval 实现重试会阻塞主线程、引发并发失控,且无法自然退出;真实弱网下连续请求会快速耗尽连接池,甚至被浏览器限流。
- 重试函数必须递归实现,例如
async function submitWithRetry(data, attempt = 1) - 首次延迟 1s,第二次 2s,第三次 4s……但要加随机抖动(jitter),防止雪崩
- 最大重试次数设为 3,超限后不再自动重试,改为提示“网络不稳定,请稍后手动重试”
- 每次重试前检查
document.hidden,页面切后台时暂停,避免锁屏后还在狂发请求
POST 类请求重试前必须确认幂等性
重复下单、重复扣款的风险真实存在。不能无脑重试,必须前置校验:要么接口本身幂等,要么前端生成唯一 Idempotency-Key 并传给服务端做识别。
- 前端生成 key 推荐用
crypto.randomUUID(),降级用Date.now() + Math.random().toString(36).substr(2, 9) - 作为请求头传入:
headers: { 'Idempotency-Key': key },别放 URL 或 body 里(易被 CDN 截断) - 按钮点击后立即设
button.disabled = true,防止连点触发多轮并发请求 - 表单数据提交前存进
localStorage,键名带时间戳(如pending_form_${Date.now()}),重试成功后立即removeItem,否则下次打开页面又触发重复提交
真正关键的只有两点:一是所有重试逻辑必须建立在 preventDefault() 拦截后的异步流程里,二是真正的断网信号永远来自 fetch 抛出的 TypeError,不是 navigator.onLine。其他设计都是围绕这两条展开的防御性补丁。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











