按钮点击后须立即禁用并显示加载状态,禁用逻辑写在事件处理函数首行;失败时需在 finally 中恢复状态;动画应轻量且兼容移动端;前后端需协同实现幂等性防重提交。

点击按钮后必须立刻禁用并切换状态,不能等请求返回再动——否则网络慢时用户会连点多次,后端可能收到重复提交。
按钮点击瞬间就该禁用并显示 loading
关键动作必须写在事件处理函数第一行,而不是塞进 fetch 或 axios.then() 里。这时候请求还没发出去,但用户交互已经发生,状态就得同步响应。
-
button.disabled = true是最直接的禁用方式,比pointer-events: none更可靠(后者不阻止键盘触发) - 文字替换建议用
button.textContent = '加载中...',避免 innerHTML 引入 XSS 风险 - 如果要用旋转图标,推荐内联 SVG 或
<div class="spinner"></div>,别依赖外部图标字体或 JS 库加载 - 用 Tailwind 的话,直接加
animate-spin类;原生 CSS 就配@keyframes spin+transform: rotate(360deg)
失败时必须手动恢复按钮状态
很多人只写成功路径,导致请求失败后按钮一直卡在 loading 状态,用户无法重试。
- 务必在
finally块里统一恢复:button.disabled = false、还原文字、移除或隐藏 loading 元素 - 不要只靠
.catch()恢复——网络中断、超时、CORS 错误等都可能跳过then,但一定会进finally - 如果用了
opacity: 0+pointer-events: none控制显隐,恢复时得同时设回opacity: 1和pointer-events: auto
loading 动画要轻量且不阻塞渲染
CSS 动画本身不卡主线程,但写法不当仍会影响体验。
立即学习“前端免费学习笔记(深入)”;
- 避免给 loading 元素加太多 class 切换,直接操作
style.opacity或style.display性能更好 - 旋转动画建议加
will-change: transform或transform: translateZ(0)触发 GPU 加速 - 移动端 Safari 15.4 以下仍需
-webkit-animation前缀,别省略 - 别用
visibility: hidden控制开关——它保留布局空间,可能撑开按钮容器造成跳动
最容易被忽略的是:按钮 loading 状态和后端幂等性必须配合。前端禁用只是第一道防线,服务端仍要靠唯一请求 ID 或 token 做去重,否则刷新页面重发请求照样会重复执行。



















