图片未写width/height必抖,因浏览器初始占位为0×0,加载后重排导致下方内容下移;必须在HTML标签中显式声明width和height属性,仅靠CSS无法在首帧预留空间。

布局抖动(CLS)不是靠 JS 监控“修好”的,而是由 HTML 结构和 CSS 行为决定的——你改了 width 和 height,CLS 就降;没改,再多监控也白搭。
为什么图片没写width/height就一定抖
浏览器解析 HTML 时,遇到没有内联 width 和 height 的 <img>,会按 0×0 占位;等图片加载完成、尺寸确定后,再重排整个流式布局——下方文字、按钮、广告位全被往下顶。这不是“慢”,是结构缺失。
-
width和height必须是 HTML 属性,写在标签里:<img src="a.jpg" width="600" height="400">;仅靠 CSSwidth: 100%或aspect-ratio不足以让浏览器首次绘制前预留空间 - 响应式场景下可配合
style="aspect-ratio: 16/9; width: 100%; height: auto;",但 Safari 15.4+、Firefox 89+ 才稳定支持;旧版仍需 fallback 到padding-top百分比占位 -
srcset+sizes不能替代width/height:浏览器可能先按默认尺寸占位,再根据sizes调整,造成两次偏移
<iframe>和第三方嵌入怎么不触发CLS
<iframe> 比图片更危险:它常加载跨域内容(YouTube、广告),且多数 SDK 默认不声明尺寸。一旦延迟加载(loading="lazy"),就会在用户滚动到视口时突然撑开布局。
- 必须显式写
<iframe width="560" height="315">,哪怕只是保守估计值;CSS 宽高无效 - 若尺寸不可预知(如动态广告),用
<div id="ad-slot" style="min-height: 250px"></div>在 HTML 源码中占位,高度取历史均值 - 避免用
visibility: hidden或opacity: 0占位——它们不占布局空间,等于没占 - 跨域 iframe 的
sources[0]?.node常为#document或null,此时要结合 Network 面板查其加载时机,而非指望 JS 定位
字体加载导致的文本重排怎么压住
字体切换本身不抖,抖的是行高、字宽突变后,容器高度塌缩或撑开——比如系统字体行高 1.2,Web Font 行高 1.4,换完直接把下面段落往下推。
立即学习“前端免费学习笔记(深入)”;
- 禁用
font-display: optional:它可能跳过加载,导致字体反复来回切,CLS 累加更猛 - 用
font-display: swap,但必须配@font-face中的size-adjust: 102%或ascent-override,对齐度量值 - 关键文本容器(
<h1>、按钮、卡片标题)预设line-height和min-height,锁住垂直空间 - 别在
font-family里混用等宽与比例字体(如"Monaco", "Helvetica"),ch/ex单位行为不一致,宽度易跳
动态插入内容如何避免“插进去就抖”
JS 插入的广告、评论、推荐模块,只要没提前占位,插入瞬间就计入 CLS——Lighthouse 常标为 <body> 贡献者,实际就是这些节点在作祟。
- 占位符必须写在 HTML 源码里,不是 JS 创建:
<div id="comments" style="min-height: 400px"></div> - 若必须 JS 注入,优先用
transform: translateY(-9999px)或position: absolute,让它脱离文档流 -
contain: layout style paint可隔离渲染影响(Chrome 85+),但前提是容器已有明确尺寸约束,否则 containment 失效 - 第三方脚本(Google Ads、客服 Widget)注入不可控,需用
PerformanceObserver监听layout-shift条目,从sources提取node?.tagName和node?.dataset.id快速定位黑盒来源
CLS 数值本身没意义,真正要紧的是哪几个元素在跳——sources 列表里第一个 node,往往就是那个没写 width/height 的 <img>,或是没占位的 <div id="ad-slot">。别盯着最终分数调优,先抓出抖的根节点,再加约束。



















