骨架屏必须随服务端html一同输出且内联,否则会导致白→灰→实三段式渲染、lcp和cls恶化;结构需与真实dom完全一致,尺寸精确、动画兼容并按加载时长智能启用。

骨架屏必须和服务端输出的 index.html 一起到达浏览器,不能靠 JS 动态插入——否则首屏白屏时间拉长,LCP 和 CLS 都会恶化。
为什么骨架屏要内联在 index.html 里
常见错误是等 JS 加载完再用 document.createElement 插入骨架结构,结果用户先看到空白页,再闪出骨架,最后才出现内容。这本质是「三段式渲染」:白→灰→实,体验比纯白屏还差。
- 服务端未输出骨架 → 浏览器解析 HTML 时无占位元素,
body内容为空,触发重排时机不可控 - 骨架结构必须和真实 DOM 的标签名、class 层级、嵌套顺序完全一致,比如真实卡片是
<article><header><h2></h2></header><img></article>,骨架就得照写,只把文本和图片替换成<div class="skeleton-title"></div>这类占位 - 所有骨架元素需声明明确宽高或
aspect-ratio,禁用min-height、fit-content或依赖字体撑开的尺寸
index.html 中骨架结构怎么写才不跳动
关键不是“看起来像”,而是“占位准”。文字行高错 1px、图片比例差 0.1,加载后就会明显“弹一下”。
- 标题行用
line-height: 1.5+ 固定height模拟,别用空格或width: 80%猜测长度 - 头像/封面图统一用
aspect-ratio: 1 / 1或16 / 9,配合background: linear-gradient(...)填充,禁用width: 100%+padding-top百分比(它在缩放 viewport 下不稳定) - 列表项用
display: flex或grid,且骨架和真实内容用同一套gap、align-items值,避免 flex 子项基线对齐偏差 - 所有骨架容器加
contain: layout paint style,但 Safari 15.4 前不支持,需包裹@supports (contain: layout) { ... }
动画卡在 Safari 或低端安卓 WebView 怎么办
直接用 background-position 循环渐变的 @keyframes shimmer 在 Safari 15.4–16.3 和部分安卓 WebView 中会掉帧甚至不触发。
- 改用
transform: translateX()+opacity组合动画,例如:@keyframes skeleton-shimmer { 0% { opacity: 0.6; transform: translateX(-100%); } 100% { opacity: 0.2; transform: translateX(100%); } } - 动画时长控制在 1.2–1.8s,太快显浮躁,太慢削弱“正在加载”暗示
- 给伪元素加
content: ""和position: absolute,父容器设position: relative,确保动画层不脱标 - 对
prefers-reduced-motion: reduce用户关闭动画:@media (prefers-reduced-motion: reduce) { .skeleton-item::before { animation: none; } }
什么时候该显示骨架屏,什么时候该跳过
骨架屏不是“有就比没有强”,它只在真实数据加载耗时超过 300ms 时才值得启用。短于这个阈值,直接渲染更自然。
- 判断依据不是“有没有请求”,而是
performance.getEntriesByType('resource')中关键接口的responseStart到responseEnd时间 - 移动端弱网(2G/3G)、SSR 首屏水合延迟高、首次访问用户可默认启用
- 老用户若历史平均加载快于 300ms,应跳过骨架,避免额外 DOM 开销和布局抖动
- HTTP 错误(如 404、503)或空数据状态,骨架屏必须不可见,改用明确错误提示或空态组件
最容易被忽略的是:骨架结构一旦写进 index.html,就必须和后续客户端渲染的真实 DOM 完全对齐——包括语义标签、box-sizing、font-size、line-height、甚至 margin/padding 的计算方式。差一点,加载完成那一刻就会“跳”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











