PerformanceObserver 监控 Layout Shift 需监听 layout-shift 类型,通过 entry.sources 定位触发元素,仅用户可交互阶段且 value ≥ 0.001 的偏移被记录,hadRecentInput 为 true 时通常不计入 CLS。

用 PerformanceObserver 监控 Layout Shift(布局偏移)的关键是监听 layout-shift 类型,并结合 sources 字段定位具体触发元素。Chrome 84+ 支持该功能,但需注意:只有用户可交互阶段(如页面加载后、滚动/点击等操作期间)发生的偏移才会被记录,且默认只上报「显著偏移」(value >= 0.001)。
启用 layout-shift 观察器并获取偏移详情
创建 PerformanceObserver 实例,传入 { type: 'layout-shift', buffered: true },并在回调中遍历 entries。每个 entry 的 sources 数组包含引发偏移的 DOM 节点信息(若可识别):
-
entry.value:本次偏移的 CLS 分值(0–1 之间) -
entry.sources:数组,每项含node(实际节点引用)、previousRect和currentRect(偏移前后的布局框) -
entry.hadRecentInput:布尔值,表示偏移是否发生在用户输入后 500ms 内(这类偏移通常不计入 CLS)
示例代码:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.hadRecentInput) continue; // 忽略用户交互导致的偏移
console.group('Layout Shift Detected:', entry.value.toFixed(4));
entry.sources.forEach(source => {
if (source.node) {
console.log('→ 触发元素:', source.node.tagName, source.node.id || source.node.className || '(no id/class)');
console.log('→ 偏移前:', source.previousRect);
console.log('→ 偏移后:', source.currentRect);
}
});
console.groupEnd();
}
});
observer.observe({ type: 'layout-shift', buffered: true });
确保能拿到真实 DOM 节点引用(source.node)
source.node 并非总存在——仅当浏览器能明确将偏移归因到某个元素时才提供。常见有节点引用的情况包括:
- 图片/iframe 加载完成导致下方内容下移
- 动态插入未设宽高的广告容器
- 字体加载完成(FOIT/FOUT)引发重排
- CSS 动画或 transition 改变尺寸(如 height: auto → 固定高度)
若 source.node 为 null,说明偏移由多个元素共同引起,或源自匿名文本节点、伪元素、CSS Grid/Auto-placement 等复杂布局逻辑。此时可结合 entry.startTime 查看当时是否有 JS 执行、资源加载或样式计算事件(配合 DevTools 的 Performance 面板交叉分析)。
主动触发并验证监控逻辑(开发调试用)
在本地测试时,可手动插入易引发偏移的元素来验证 observer 是否生效:
- 添加无尺寸的图片:
<img src="placeholder.jpg">(缺 width/height) - 动态插入广告 div:
document.body.insertAdjacentHTML('beforeend', '<div class="ad"></div>');,随后通过 JS 设置其高度 - 模拟字体加载延迟:用
@font-face+font-display: optional,再切换文字内容
注意:部分偏移需在真实用户场景(如弱网、低端设备)下才明显,建议搭配 chrome://flags/#enable-layout-shift-regions(开启布局区域高亮)辅助定位。
与 Cumulative Layout Shift(CLS)指标联动分析
单次 layout-shift entry 不代表最终 CLS,CLS 是会话内所有非输入相关偏移的累计值。可通过全局变量累加:
let cumulativeShiftScore = 0;
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
cumulativeShiftScore += entry.value;
console.log('CLS so far:', cumulativeShiftScore.toFixed(4));
}
}
});
observer.observe({ type: 'layout-shift', buffered: true });
还可结合 navigation 或 visibilitychange 事件,在页面隐藏/跳转前上报最终 CLS,用于性能埋点。


















