关键不是动画而是结构占位,须用真实HTML元素模拟宽高、行高、圆角、间距等DOM结构,再叠加linear-gradient+background-position闪光动画,首屏骨架CSS必须内联以避免白屏。
骨架屏不是“动起来就完事”,而是让内容区域在加载前保持稳定尺寸和布局。否则 height 塌陷、文字闪跳、按钮错位,用户反而更困惑。必须先用透明/灰阶色块模拟真实 DOM 结构的宽高、行高、圆角、间距——动画只是叠加在上面的一层视觉反馈。
常见错误:直接给整个容器加 background: linear-gradient(...) 并 animate background-position,但没控制子元素的 height 和 margin,导致骨架“浮”在空白里,和后续真实内容对不齐。
<div class="skeleton-title"></div>),而不是靠伪元素硬撑width、height、border-radius,和最终内容一致<div class="skeleton-line"></div> 模拟,避免用单个 div + line-height 拉伸,那样响应式时容易错位background-image + background-position 最稳不要用 opacity 闪烁或 transform: translateX 滑动——前者无方向感,后者在低性能设备上掉帧明显。真正的“光扫过”效果,靠线性渐变背景 + 水平位移最轻量、最可控。
核心代码就这三行,其余都是适配:
立即学习“前端免费学习笔记(深入)”;
background: linear-gradient(90deg, transparent, #e0e0e0, transparent);background-size: 200% 100%;animation: shine 1.5s infinite;
注意:background-size 必须设为 200% 100%,否则动画会卡顿或循环突兀;animation-duration 控制在 1.2–2s 之间,太快显得焦躁,太慢失去“加载中”暗示。
@keyframes shine 显式定义,不能依赖浏览器默认行为overflow: hidden,否则闪光会溢出边框#e0e0e0 换成 #333 等对应灰度,别依赖 prefers-color-scheme 自动切换,兼容性差v-if 或 display: none
骨架屏要“无缝交接”,意味着它和真实内容必须占据完全相同的文档流位置。用 v-if 切换、或用 display: none 隐藏骨架,会导致 DOM 节点销毁重建,页面重排、滚动位置丢失、甚至触发 SSR 和 CSR 不一致警告。
正确做法是始终保有骨架结构,仅通过 opacity 和 pointer-events 控制交互状态:
opacity: 1,真实内容 opacity: 0; pointer-events: none;
opacity: 0; pointer-events: none;,真实内容 opacity: 1;
transition: opacity 0.2s ease-in-out,别加 visibility,它不触发重绘React 用户注意:不要在 useEffect 里手动 removeChild 骨架节点——那等于自己制造水合不匹配。
骨架屏最大的价值在首屏白屏期。如果骨架样式写在外部 CSS 文件里,或靠 JS 动态插入,那在资源未加载完前,用户看到的还是纯白屏——你做的所有动画都白搭。
实操底线:
<style> 标签,包含所有骨架相关类(.skeleton-*)的 height、background、animation
mini-css-extract-plugin 提取到独立文件;Vite 用户可配 build.rollupOptions.output.inlineDynamicImports = true 辅助真正难的不是怎么动,是怎么让骨架在 JS 还没执行、CSS 还没解析完的时候,就已经稳稳立在那里。这点常被忽略,却直接决定用户第一眼是否觉得“页面卡了”。