class 本身不触发动画,而是通过切换 class 触发浏览器重算样式,从而启动已绑定的 css 动画或过渡;需分层命名(如 skeleton/is-loading/animate-pulse)、固定骨架尺寸、仅用 opacity/transform 等可加速属性,并在 ssr 中延迟添加动效 class 以避免闪烁。

class 属性本身不触发动画,骨架屏过渡靠的是 class 切换配合 CSS 动画规则,不是 class 自带动效。
为什么直接改 class 就能触发动画
CSS 动画(@keyframes)必须绑定到具体选择器上,而 class 是最轻量、最可控的切换开关。浏览器在 DOM 节点的 class 列表变化时,会立即重新计算样式,只要新 class 对应的 CSS 规则含 animation 或 transition,动画就启动。
- 动画是否播放,取决于「当前生效的 CSS 规则」,而非
class本身 - 用
classList.add()/classList.remove()切换 class 比操作style更利于解耦和复用 - 避免直接写
element.style.animation = '...'——这样会覆盖其他动画、难维护、无法利用 CSS 变量控制时长或延迟
class 命名要区分「状态」和「动效」
常见错误是把状态和动画混在一个 class 里,比如 loading 同时控制显隐 + 骨架条颜色 + 闪烁动画。一旦需要“加载完成但保留骨架结构”或“暂停动画”,就束手无策。
- 推荐分层命名:
skeleton(结构)、is-loading(状态)、animate-pulse(动效) -
is-loading控制 opacity / visibility;animate-pulse单独定义animation: pulse 1.5s ease-in-out infinite; - 这样可组合:
<div class="skeleton is-loading animate-pulse">,后续只需删 <code>is-loading和animate-pulse即可平滑退出避免重排导致动画卡顿的关键细节
骨架元素如果频繁触发 layout(比如用
height: auto或依赖内容撑开),动画过程中浏览器要反复测量尺寸,帧率直线下跌。- 骨架容器必须设固定宽高,或用
aspect-ratio(现代浏览器)+min-height - 禁用
will-change: transform在 skeleton 元素上——它对纯颜色/透明度动画无益,反而增加图层合成开销 - 动画属性优先用
opacity和transform(如translateX模拟光栅扫过),避开width、height、left、top - 示例动效 CSS:
`.animate-pulse { animation: skeleton-shimmer 2s infinite; } @keyframes skeleton-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }`——这里只动background-position,GPU 可加速
服务端渲染(SSR)下 class 切换的时机陷阱
Node.js 环境没有
document,初始 HTML 里若直接写class="skeleton is-loading animate-pulse",首屏会闪一下动画,等 JS 加载后才移除——用户看到的是“先动再停”,体验割裂。- SSR 时只输出
class="skeleton",不带任何状态或动效 class - 前端 hydration 完成后,JS 主动加
is-loading和animate-pulse,确保动画只在客户端启动 - 用
requestIdleCallback或setTimeout(..., 0)延迟添加动效 class,避免和 hydration 抢占主线程
真正难的不是写几个 class 或 animation,而是让 class 的增删节奏和数据加载生命周期严丝合缝——差一帧,用户就感知为卡顿或闪烁。
- 骨架容器必须设固定宽高,或用











