按钮点击瞬间立即添加loading状态并禁用默认提交,通过css伪元素+gpu加速旋转动画实现流畅加载效果,所有异步出口必须手动清除loading状态并同步更新aria属性。

点击瞬间就加旋转图标,别等请求返回
按钮 loading 状态必须在 click 事件第一行触发,不是等 fetch() 或 axios.post() 返回才加动画。网络延迟时用户狂点,后端可能收到重复请求——哪怕你做了幂等校验,也增加了无谓负载。
- 立即执行:
buttonEl.classList.add('loading')+buttonEl.setAttribute('aria-busy', 'true') - 同步禁用表单默认提交:
event.preventDefault()(如果在<form></form>内) - 不要写:
buttonEl.disabled = true——它会让 CSS 动画卡住、:hover失效、屏幕阅读器跳过该控件
.loading 类里用 transform: rotate() + will-change
直接对按钮或其子元素(比如内联 <svg></svg>)加旋转动画,关键不是“转起来”,而是转得顺、不卡、不锯齿。纯 @keyframes spin { to { transform: rotate(360deg); } } 在 Safari 上容易跳回 0 度,低端安卓也可能掉帧。
- 必须显式写全关键帧:
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } - 动画元素上加
transform: translateZ(0)或will-change: transform触发 GPU 加速 - 避免在旋转元素上同时改
width、margin、top——这些会强制重排,动画一卡一卡 - SVG 图标务必设整数尺寸:
width="24",别用23.5,否则旋转时边缘发虚
用 ::after 插旋转图标比改文字更可控
很多人喜欢点击后把按钮文字改成“加载中…”,但文字长度变化会引发布局抖动,且无法和图标动画同步;用伪元素插入旋转 SVG 更轻量、更稳定。
- CSS 中定义:
.btn.loading::after { content: ''; display: inline-block; width: 16px; height: 16px; background: url("data:image/svg+xml,..."); animation: spin 0.8s linear infinite; } - 注意:别写
content: ""+display: block——改用inline-block或vertical-align: middle对齐文字 - 若用内联 SVG,记得加
shape-rendering="crispedges"防锯齿 - 动画时长控制在
0.8s ~ 1.2s:太短像抽搐,太长让用户焦虑
失败后必须手动清理 loading 状态
只在成功回调里恢复按钮是常见疏漏。网络中断、4xx/5xx 响应、甚至用户切页都可能导致请求没走到 .then(),按钮就永远卡在 loading 态。
- 所有异步出口都要处理:
.catch()、.finally()、AbortSignal 的abort事件 - 还原操作要完整:
buttonEl.classList.remove('loading')+buttonEl.removeAttribute('aria-busy')+buttonEl.removeAttribute('aria-disabled') - 别依赖 CSS 自动消失——JS 层必须明确控制状态生命周期
aria-busy 和 pointer-events: none 的配合使用。前者告诉读屏器“正在忙”,后者阻止二次点击,但两者缺一不可;少设一个,就等于在可访问性和防重复提交之间挖了个坑。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











