字节笔记本字节笔记本

使用 IntersectionObserver 优化 Web 性能

2024-07-03

`IntersectionObserver` 是一种现代浏览器 API,通过懒加载图像、实现无限滚动等功能,显著优化了 Web 性能,提升了首次内容绘制(FCP)、最大内容绘制(LCP)和累计布局偏移(CLS)的用户体验。

IntersectionObserver 是一种现代浏览器 API,用于检测元素何时进入或离开视口(viewport)。它适用于懒加载图像、实现无限滚动、记录广告曝光率等场景。通过使用 IntersectionObserver 可以显著优化首次内容绘制(FCP)、最大内容绘制(LCP)和累计布局偏移(CLS)。

首次内容绘制(FCP)

定义:
首次内容绘制是指浏览器首次绘制来自 DOM 的任何文本、图像(包括背景图像)、非空白的 <canvas> 或 SVG 内容的时间点。

重要性:
FCP 衡量用户在加载页面后看到任何内容所花的时间,是用户体验的关键组成部分,因为它表明页面开始呈现内容。

目标:
为了提供良好的用户体验,FCP 应在 1.8 秒或更短时间内发生。

优化 FCP 的方法:

  1. 减少阻塞渲染的 JavaScript 和 CSS。
  2. 使用内容分发网络(CDN)加快内容传递。
  3. 优化字体加载,使用 font-display: swap

最大内容绘制(LCP)

定义:
最大内容绘制是指页面可视区域内最大的图像或文本块完全加载并呈现的时间点。它通常包括大的图像、视频帧、或大块的文本。

重要性:
LCP 直接关系到用户感知的加载速度。相比于其他指标,LCP 更接近用户的真实体验,表明页面的主要内容何时完成加载。

目标:
为了提供良好的用户体验,LCP 应在 2.5 秒或更短时间内发生。

优化 LCP 的方法:

  1. 优化和压缩图像和视频。
  2. 提前加载关键资源。
  3. 减少服务器响应时间。
  4. 最小化渲染阻塞资源。

累计布局偏移(CLS)

定义:
累计布局偏移是指整个页面加载过程中发生的所有意外布局偏移的总和。这些偏移是用户在页面上看到内容突然移动的现象,可能是由于延迟加载的资源或动态内容导致的。

重要性:
CLS 反映了页面的视觉稳定性。高的 CLS 值表示页面内容在加载过程中发生了大量的意外移动,可能导致糟糕的用户体验。

目标:
为了提供良好的用户体验,CLS 应保持在 0.1 或更低。

优化 CLS 的方法:

  1. 为图像和视频预留固定尺寸。
  2. 避免在页面顶部插入新内容。
  3. 使用 CSS 动画代替属性变化来实现视觉效果。
  4. 避免第三方脚本造成的布局偏移。

使用 IntersectionObserver 进行懒加载

基本用法

  1. 创建 IntersectionObserver 实例:
const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // 元素进入视口
      loadImage(entry.target);
      // 停止观察此元素
      observer.unobserve(entry.target);
    }
  });
}, {
  root: null, // 默认为视口
  rootMargin: '0px', // 视口外部的 margin
  threshold: 0.1 // 触发回调的阈值,0.1 表示元素 10% 可见时触发
});
  1. 观察元素:
const imgElements = document.querySelectorAll('img[data-src]');
imgElements.forEach(img => {
  observer.observe(img);
});
  1. 执行懒加载操作:
function loadImage(img) {
  const src = img.getAttribute('data-src');
  if (!src) {
    return;
  }
  img.src = src;
  img.removeAttribute('data-src');
}

示例:懒加载图片

HTML:

<img data-src="path/to/image1.jpg" alt="Lazy loaded image 1">
<img data-src="path/to/image2.jpg" alt="Lazy loaded image 2">
<img data-src="path/to/image3.jpg" alt="Lazy loaded image 3">

JavaScript:

document.addEventListener('DOMContentLoaded', () => {
  const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        const src = img.getAttribute('data-src');
        if (src) {
          img.src = src;
          img.removeAttribute('data-src');
        }
        observer.unobserve(img);
      }
    });
  }, {
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  const imgElements = document.querySelectorAll('img[data-src]');
  imgElements.forEach(img => {
    observer.observe(img);
  });
});

高级用法

  1. root:用作视口的元素,如果未设置或为 null,则默认使用浏览器的视口。

  2. rootMargin:用于扩大或缩小视口的边界。可以用类似 CSS margin 的值,例如 "0px 0px 100px 0px",表示视口底部扩大 100px。

  3. threshold:定义触发回调的可见度百分比。例如 [0, 0.5, 1] 表示在元素完全不可见、50% 可见和完全可见时触发回调。

const observer = new IntersectionObserver(callback, {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px 0px 200px 0px',
  threshold: [0, 0.5, 1]
});

结论

通过使用 IntersectionObserver,可以高效地实现懒加载和其他与视口相关的功能,从而提升网站的加载性能和用户体验。这些优化方法可以显著改善首次内容绘制(FCP)、最大内容绘制(LCP)和累计布局偏移(CLS),提供更好的用户体验。