骨架屏图片占位必须设aspect-ratio或padding-top+绝对定位保比例,禁用img/svg、height:100%及动态class,用linear-gradient流光动画并加overflow:hidden,ssr需dom完全同构。

图片降级不是加个 alt 或 fallback onerror 就算完事——骨架屏里的图片占位,必须在真实图片加载前就稳住布局、不抖、不塌、不触发额外请求,且服务端和客户端 DOM 结构要完全一致。
为什么 skeleton-img 一渲染就塌成一条线
90% 是因为只写了 background-color 却没设尺寸约束。空 <div> 默认是 0×0,浏览器无法推断宽高比,后续图片插入必然重排(CLS)。现代方案必须用 <code>aspect-ratio;兼容旧版得靠 padding-top + position: relative + 绝对定位子元素。
-
aspect-ratio: 16 / 9是首选,配合width: 100%和overflow: hidden,无需 JS 干预 - 旧 Android WebView 或 IE11 不支持
aspect-ratio,改用:padding-top: 56.25%(16:9 对应值),父容器设position: relative,子元素position: absolute; top: 0; left: 0; width: 100%; height: 100% - 禁用
height: 100%—— 父容器没高度时它直接坍缩为 0,骨架“消失” - 所有骨架图片占位统一用
<div class="skeleton-img"></div>,不带src、alt、aria-label,避免读屏器误读或资源加载
怎么让占位图看起来像“正在加载”的内容
纯灰色块太死板,用户感知不到“流动感”。用 CSS 渐变 + 背景位移动画最轻量、最稳定,且 GPU 合成友好,不卡顿。
- 背景写法必须是:
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%),两个同色端点不能省,否则 IE9/旧 WebView 渲染异常 -
background-size: 200% 100%+background-position: -200% 0配合@keyframes横向移动,比opacity或transform更稳 - 动画周期控制在
1.4s,animation-timing-function: linear,避免ease-in-out停顿感 - 低端设备加兜底:
@media (prefers-reduced-motion: reduce) { animation: none; } - 务必加
overflow: hidden,否则渐变会溢出圆角或边框
服务端吐出的骨架 HTML 怎么和客户端真实 DOM 对齐
SSR 场景下 hydration 错位,八成是因为骨架和真实组件的 DOM 结构不一致:class 名拼错、层级多一层、子节点数量不同、甚至多了个空格文本节点。
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
- 服务端模板中骨架必须和真实组件使用**完全相同的标签名、class 名、嵌套层级、子节点数量**,比如真实卡片是
<article class="card"></article>,骨架就得是<article class="card skeleton-card"></article>,而不是另起一套skeleton-article - 禁用动态 class 拼接:
class="card {{ loading ? 'skeleton' : '' }}"—— 服务端永远只渲染 skeleton 版本,客户端 hydrate 时 class 必须字面完全匹配 - 骨架 DOM 必须被**彻底卸载**(
element.remove()),不能靠display: none或v-show隐藏,否则仍参与布局计算、影响无障碍树、拖慢 hydration - SSR 模板中骨架结构需在首段 flush 输出,Node.js Express 下用
res.write()先发骨架,再流式输出真实内容
懒加载图片的占位怎么防抖动
只靠 loading="lazy" 或 IntersectionObserver 监听,不代表能跳过占位逻辑。空白 src 或 base64 在部分 Android WebView 中解析失败,导致容器塌陷,文字上推。
- 懒加载图片的
<img>的src必须指向一个真实存在的小图,如/assets/placeholder-320x240.png(1–2KB,尺寸严格匹配目标图) - 禁止用
data:image/png;base64,...当首屏占位,它仍触发解码、占用主线程,且尺寸精度难控 - JS 加载成功后,要显式替换
src并移除lazy类,否则IntersectionObserver可能重复触发 - 若用
onerror做 fallback,必须防循环:onerror="if (this.src !== '/fallback.png') this.src = '/fallback.png';"
真正难的不是写出骨架样式,而是让骨架在 SSR、hydration、懒加载、动画、可访问性这五条线上同时不翻车——任何一个环节尺寸没对齐、DOM 没同构、动画属性选错,都会导致闪、抖、塌、卡、读错。










