cls 降低的关键在于浏览器能否提前预知元素尺寸;所有 和 必须显式设置 html width/height 属性,仅 css 无效,响应式需搭配 aspect-ratio 并 fallback 像素值,动态内容须在 html 源码中用 min-height 预留占位,字体加载需 font-display: swap 配合 size-adjust 对齐度量。

CLS 不是靠“代码质量高”就能自动降下来的指标——它只认一个事实:浏览器在渲染时,是否能提前知道每个元素该占多大地方。HTML 代码里没写 width 和 height,哪怕 CSS 写得再规范、语义再正确,<img> 一加载,CLS 就跳。
所有 <img> 和 <iframe></iframe> 必须带内联 width/height
浏览器解析 HTML 是流式的,遇到没尺寸的 <img>,直接按 0×0 占位;等图片下载完、解码完、才重排一次——下方文字/按钮全被往下顶,这就是最典型的 CLS 来源。
-
width和height必须是 HTML 属性,不是 CSS;style="width:100%; height:auto"完全无效 - 响应式场景下可搭配
style="aspect-ratio: 16/9; width: 100%; height: auto",但 Safari 15.4+、Firefox 89+ 才稳定支持,旧版仍需 fallback 到像素值 -
srcset+sizes不能替代width/height:浏览器可能先用默认尺寸占位,再根据sizes调整,造成两次偏移 -
loading="lazy"和尺寸声明必须共存;懒加载不解决占位问题,只延迟加载时机
动态内容注入前,HTML 源码里就得有占位容器
JS 插入广告、评论、推荐卡片时,如果 DOM 里原本是空的,新节点一挂上,整个文档流就往下挤。Lighthouse 报告里常标 贡献 CLS,实际就是这类“空容器”在作祟。
- 占位容器必须带
min-height(如style="min-height: 250px"),高度尽量接近历史均值,不能靠visibility: hidden或opacity: 0——它们不占空间 - 第三方脚本(如 Google AdSense)注入的内容,务必手动包裹一层固定高容器,否则无法控制
- React/Vue 组件首次挂载若含未约束尺寸的子元素(比如没设
height的<div>),hydration 后照样抖;SSR/SSG 阶段就要把骨架结构写进 HTML <li>用 <code>contain: layout style paint可隔离区域影响,但前提是容器本身已有明确尺寸约束(Chrome 85+ 支持) - 禁用
font-display: optional:它可能跳过加载,导致字体反复切换,CLS 更糟 - 优先用
font-display: swap,但必须配合@font-face中的size-adjust(如size-adjust: 102%)或descent-override对齐度量值 - 关键容器(如
<h1></h1>、CTA 按钮)预设line-height和min-height,防止字体切换时高度塌缩或撑开 - 避免在
font-family列表里混用等宽与比例字体(如"Monaco", "Helvetica"),ch/ex单位行为不一致,易引发宽度跳变
字体加载引发的文本重排,要靠 font-display + 尺寸对齐双控
自定义字体加载期间用系统字体渲染,加载完再换——如果两套字体行高、字宽差异大,段落高度突变,就会把下面内容往下顶。这不是“字体慢”,而是尺寸不可预测。
CLS 最容易被忽略的点,是它不只发生在首屏加载完成那一刻——用户滚动、交互、JS 持续插入 DOM 时,只要元素位置发生非预期变化,就会继续累积。一个没设 min-height 的广告位,哪怕在页面底部,用户滑到那儿才加载,照样拉高 CLS。











