骨架屏HTML必须与服务端同构,否则hydration错位导致闪屏、焦点丢失;需严格一致标签名、class、层级、子节点数;禁用动态class拼接;图片占位用aspect-ratio或padding-top hack;动画仅用transform和background-position;骨架DOM须彻底卸载而非隐藏。

骨架屏 HTML 必须和服务端输出同构,否则 hydration 会错位
Vue/React 的 SSR 渲染依赖 DOM 结构严格一致。如果服务端吐出的骨架是 <div class="card"></div>,而客户端 hydrate 时真实内容是 <article class="card"></article>,React 就会丢弃整个节点树重挂载——结果就是内容闪一下、焦点丢失、表单值清空。
- 检查服务端模板中骨架节点的标签名、class 名、嵌套层级、子节点数量,必须和真实组件 render 出的 DOM 完全一致
- 避免用动态拼接 class:
class="card {{ loading ? 'skeleton' : '' }}"—— 服务端永远只渲染 skeleton 版本,客户端 hydrate 时 class 必须完全匹配 - 推荐方案:骨架用
<div class="card skeleton-card">,真实内容用<div class="card real-card">,靠 CSS 控制显隐,但 DOM 结构不变 - 特别注意注释节点、空格文本节点——它们在 SSR 中可能被保留,在客户端却被忽略,导致结构差一层
图片占位必须用 aspect-ratio 或 padding-top hack,禁用 height: 100%
height: 100% 在父容器没高度时坍缩为 0,骨架“消失”,真实内容插入后立刻重排。这不是视觉问题,是布局塌陷。
- 现代浏览器优先用
aspect-ratio: 16 / 9(或对应比例),配合width: 100%和overflow: hidden - 兼容旧浏览器时用
padding-top: 56.25%+position: relative,再用绝对定位子元素撑满 - 绝对禁止写
<img src="data:image/svg+xml...">—— 它仍触发解码、占用主线程,且无法控制尺寸精度 - 所有图片占位统一用
<div class="skeleton-img"></div>,不带任何语义属性(alt、src、aria-label)
动画只能用 background-position 和 transform,其他属性会卡顿
在低端设备上,left、top、width、opacity 触发重排或高开销重绘,动画掉帧、LCP 恶化、iOS Safari 卡死都是常见后果。
- 标准写法:用
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)+background-size: 200% 100%+animation: loading 1.4s infinite linear - 必须加
will-change: transform(对transform动画)或提前声明合成层,避免动画启动时临时提升图层抖动 - 所有骨架元素设
overflow: hidden—— 否则渐变背景溢出圆角,破坏视觉一致性 - 动画周期控制在 1.2–1.8s:太快用户感知不到加载过程,太慢显得卡顿;禁用
ease,用linear保证匀速流动感
骨架 DOM 必须卸载,不能靠 v-show 或 display: none 隐藏
隐藏 ≠ 移除。用 v-show、visibility: hidden 或 opacity: 0 会让骨架节点持续参与布局计算、占用无障碍树、拖慢页签性能,更关键的是——它破坏 hydration 一致性。
立即学习“前端免费学习笔记(深入)”;
- 切换时机:真实内容 mount 完成、数据 ready 后,调用
element.remove()彻底销毁骨架 DOM 节点 - 不要用
innerHTML = ''再拼字符串替换 —— 会丢事件监听、滚动位置、表单状态;推荐用replaceWith(realElement) - 所有骨架节点加
data-skeleton="true",便于 JS 精准定位并移除,避免残留 - 无限滚动场景下,每批骨架项必须和对应真实列表项结构完全对齐:class 名、
data-id、子元素数量、甚至注释位置都应一致



















