启用 buffered: true 才能捕获页面加载早期的布局偏移;优先通过 sourceElement 定位偏移源,fallback 到 affectedElements;过滤非用户触发且视口内的偏移(value > 0.01);按秒聚合上报,避免阻塞渲染。

直接用 `PerformanceObserver` 监听 `layout-shift` 类型事件,就能捕获每次布局偏移的原始数据;但要自动关联导致偏移的元素,不能只靠监听本身——得结合 `entry.sourceElement`、`entry.affectedElements` 和视口位置做判断,并注意注册时机和上报方式。
必须开启 buffered: true 才能拿到首屏偏移
页面加载早期(尤其是首屏渲染阶段)发生的布局偏移最容易被漏掉。如果监控脚本异步加载或注册太晚,`PerformanceObserver` 默认不会回溯已发生的事件。
- 注册时一定要传 `{ type: 'layout-shift', buffered: true }`
- 这会让浏览器把当前会话中尚未被垃圾回收的 layout-shift 条目推入回调,覆盖从导航开始到用户离开前的关键时段
- 不支持跨页面或 SPA 路由切换延续,每个新页面都需重新注册 observer
定位“谁在跳”:优先看 sourceElement,再 fallback 到 affectedElements
`entry.sourceElement` 是最直接线索,指向最后修改 DOM 并引发偏移的元素(比如一张刚加载完成的 ``)。但它可能为空——例如由 CSS 动画、JS 修改 `transform` 或匿名文本节点变化引起时。
- Chrome 120+ 支持 `entry.affectedElements`,返回一个 `Element[]`,更贴近实际发生位移的元素集合
- 若两者都不可用,可结合 `entry.startTime` 和 DevTools 的 Rendering → Layout Shift Regions 定位时间点附近的 DOM 变更
- 别只按 `tagName` 归类:`
` 常是表象,根因可能是父容器缺失 `aspect-ratio` 或未设宽高
过滤出真正影响体验的偏移
不是所有 layout-shift 都该报警。CLS 只统计「非用户触发 + 视口内可见」的偏移,单次 value > 0.01 就值得重点关注。
- 跳过 `entry.hadRecentInput === true` 的条目(如点击后展开菜单引起的位移)
- 用 `elem.getBoundingClientRect()` 判断元素是否在视口内,排除滚动外区域的抖动
- 关注 `entry.value` 分布:累加得到当前 CLS,但更要盯住单次 > 0.05 的异常片段
上报要聚合,不能逐条发请求
一次图片加载或广告注入可能触发几十次小偏移。逐条 fetch 上报不仅易被节流,还可能阻塞主线程。
- 按秒级时间戳(`Math.floor(entry.startTime / 1000)`)分桶聚合
- 每秒最多上报一次,含:最大单次 value、涉及元素个数、`sourceElement.tagName` 分布、一个典型元素的序列化快照
- 用 `requestIdleCallback` 或 `setTimeout(..., 0)` 延迟执行,避免影响渲染










