cls 不是 bug,而是 html 缺乏尺寸确定性所致;img/iframe 必须带 width/height 属性,svg 需 viewbox 与显式宽高,动态内容需硬占位,字体需 size-adjust 对齐,嵌套滚动应避免 body 宽度突变。

CLS 不是 bug,是浏览器在告诉你:HTML 结构没给它足够确定性信息。 所有“抖动”都发生在首次绘制(first paint)之后、资源加载完成之前这个窗口期——根源不在 JS 或 CSS 动画,而在 HTML 元素是否声明了可预测的尺寸与行为边界。
img 和 iframe 必须带 width/height 属性,仅 CSS 无效
浏览器解析 HTML 时,<img> 或 <iframe></iframe> 没有 width 和 height 属性,就按 0×0 或默认行内尺寸占位。等资源加载完成才重排,下方文字/按钮全被顶下去——这是 CLS 最高频贡献源。
- 必须写成
<img src="a.jpg" style="max-width:90%" style="max-width:90%">,哪怕只是占位数值;style="width: 100%; height: auto"或aspect-ratio: 16/9是补救,不是替代 -
srcset+sizes场景下仍要配width/height,否则浏览器可能先按默认尺寸占位再调整,造成两次偏移 -
loading="lazy"会放大风险:懒加载图片更可能在用户滚动到视口时才触发重排,抖动感知更强 - SVG 同理,必须带
viewBox,且显式设width/height,否则旧版 Safari 可能 fallback 到 300×150 默认渲染再缩放
动态内容注入前必须预留硬性占位空间
JS 插入广告、评论、推荐模块时,若父容器无明确高度约束,插入瞬间就会挤压流内元素——Lighthouse 常标为 贡献 CLS,实际就是这类元素在作祟。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 在 HTML 源码中就写好占位:
<div id="ad-slot" style="min-height: 250px"></div>,高度尽量接近历史均值 - 避免用
visibility: hidden或opacity: 0占位——它们不脱离文档流,但无尺寸,等于没占 - 骨架屏比纯空容器更优:提供视觉预期,且自身结构稳定,不依赖内容尺寸
- 第三方组件(如 cookie banner)优先用
position: fixed或position: sticky,彻底脱离文档流
字体加载引发的文本回流必须用 size-adjust 控制
font-display: swap 能避免阻塞渲染,但回退字体和自定义字体的行高、字宽差异仍会导致文本重排——尤其在 <h1></h1>、按钮内文字上,视觉跳动明显。
- 禁用
font-display: optional:它可能跳过加载,导致字体反复切换,CLS 更糟 - 必须配合
@font-face中的size-adjust(如size-adjust: 102%)或ascent-override对齐度量值 - 关键文案区域(如 CTA 按钮)避免使用未做尺寸对齐的 Web Font
- 系统字体栈要尽量匹配:自定义字体是无衬线体,回退链里就别混用等宽字体(如
"Monaco", "Helvetica"),ch/ex单位行为不一致易引发宽度跳变
嵌套滚动容器会让 body 宽度突变,抖动照常发生
当一个大块内容包裹在 <div class="wrapper"> 里,而这个 wrapper 又被另一个 <code><div class="page"> 包着,且后者设置了 <code>overflow: auto,那么只要内容高度跨过视口,滚动条就会在局部容器内出现——但它实际占用的是整个视口宽度。
- 真正起作用的只有
元素上的overflow-y: scroll,或任意嵌套<div> 加了都没用,反而可能造成双滚动条 <li>设计不允许常驻滚动条时,应避免「全局内容区 + 局部滚动容器」混用</li> <li>更稳妥做法是把可滚动区域严格限定在 <code><main></main>或<article></article>内,其余区域(如 header/footer)用position: sticky或脱离文档流 - 微信小程序或 Vue 自定义滚动组件(如
el-scrollbar)内部用overflow: hidden+ JS 模拟滚动,但若里面塞了position: fixed元素,结构上就等于主动邀请抖动入场
最常被忽略的不是某一行代码写错,而是把“让浏览器知道尺寸”这件事,当成 CSS 工作而非 HTML 责任——width/height 属性写在 HTML 里,才是浏览器最早能拿到的确定性信号;所有后续样式补救,都是在弥补这个初始信息缺失。










