按钮点击后必须同步设置 disabled 属性阻断二次提交,立即切换文案或显示纯 CSS 旋转 loading 圆圈,失败后手动恢复 disabled 状态,并确保移动端 :active 反馈与卸载保护。

点击后按钮变 loading 状态,但不能重复提交
关键不是加个动画,而是阻断二次点击。原生 button 的 disabled 属性必须同步启用,否则用户狂点还是能发多次请求。
- 用
button元素,别用div或a模拟——语义和可访问性都垮了 - 点击后立即设
disabled="true",同时切换文案或插入 loading SVG - 不要靠 CSS 动画“假装”禁用,得真禁用——否则键盘用户仍可用空格/回车触发
- 请求失败后记得手动清掉
disabled,不然按钮永远卡死
用 CSS 实现简洁的 loading 圆圈,不依赖第三方库
一个纯 CSS 的旋转圆圈足够应付大多数场景,体积小、兼容好、无 JS 依赖。
- 用
@keyframes定义旋转动画,transform: rotate(360deg) - 给按钮内嵌一个
<span class="loading"></span>,默认display: none - 激活时显示该
span,并给它加animation: spin 1s linear infinite - 注意:iOS Safari 对
transform-origin默认值敏感,显式设center更稳
React 里状态管理 loading 的常见翻车点
很多人用 useState 控制 isLoading,但漏掉异步链路中的清理逻辑,导致组件卸载后 setState 报错。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 用
useEffect清理副作用时,要判断组件是否已卸载:let isMounted = true+return () => { isMounted = false } - 避免在事件处理函数里直接写
setIsLoading(true)→await api()→setIsLoading(false),因为 await 后可能组件已销毁 - 推荐封装一个
useAsyncHook,统一处理 pending / fulfilled / rejected 状态和卸载保护 - 别把 loading 状态存在全局 store 里——按钮级状态就该在局部组件里闭环
移动端点击无反馈?加 loading 前先解决 active 状态
很多按钮在 iOS 上点击没压感,不是动画问题,是 :active 样式被忽略或未触发。
立即学习“前端免费学习笔记(深入)”;
- 确保按钮有
cursor: pointer和-webkit-tap-highlight-color: transparent - 给
button:active加背景色或缩放变化,让用户感知“已按” - 如果用了
pointer-events: none模拟禁用,会同时干掉:active,改用disabled才可靠 - 某些安卓 WebView 对
transition和transform同时存在时动画卡顿,loading 圆圈优先用animation而非transition


















