懒加载(Lazy Loading),也称为延迟加载,是 Web 开发中常用的优化技术。其基本原理是延迟加载页面上的资源(如图片、脚本、样式表等),直到这些资源真正需要显示或使用时才进行加载。这种技术可以显著提升页面的加载速度,减少初始加载时的网络流量,提高用户体验。

核心思想是最开始只加载对用户可见或即将可见的内容,其他内容则等到用户滚动页面到达相应位置时再加载。对于图片、视频等媒体资源,这意味着当它们即将进入视口(viewport)时才开始加载;对于 JavaScript 或 CSS 文件,可以在满足某些条件(如用户交互)后再加载和执行。

实际应用时,对于图片资源,建议使用 Intersection Observer API,用于异步观察目标元素与其祖先元素或顶级文档视窗的交叉状态。在 HTML 中,图片的 src 属性最开始设置为空,或一个轻量级的占位图,真是的图片 URL 放在 data-src 属性中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

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

在这段示例中,首先创建一个 IntersectionObserver 实例。IntersectionObserver接受一个回调函数作为参数,该回调函数会在观察的元素进入或退出视口时被调用。此外,回调函数接收两个参数:entriesobserverentries是一个数组,包含了所有被观察元素的 IntersectionObserverEntry 对象。observer 是观察者实例本身。

回调函数遍历entries数组,对每个entry,检查其isIntersecting属性是否为true,即该元素是否进入了视口。

如果 true,说明元素已经进入视口或在视口中,此时会执行以下操作:

  • entry.target 获取当前被观察的元素(在这个场景中是<img>元素)。
  • <img> 元素的src属性设置为 data-src 属性中存储的真实图片URL。data-src属性在HTML中预先定义,存储了图片的实际路径。
  • 调用observer.unobserve(img)停止观察当前元素,避免重复加载图片。

最后,使用 document.querySelectorAll 选择页面上所有带有 data-src 属性的 <img> 元素,这个属性存储了要懒加载图片的 URL。然后,遍历这些图片元素,并使用前面创建的 IntersectionObserver 实例开始观察它们。这样,一旦这些图片即将进入视口,就会自动加载它们的图片资源。

不过在使用懒加载时要注意,为了获得更好的视觉观感,需要考虑以下几点:

  1. 加载占位符:在图片资源实际加载完成前,最好使用一个占位图或加载动画。这可以提供更平滑的用户体验,避免页面布局突然变化导致的视觉闪烁。占位符可以是一个轻量级的图片、一个纯色背景、或者是表示加载中状态的动画。
  2. 图片尺寸:如果图片的尺寸在页面上已经预留,即使图片还未加载,页面布局也不会发生变化,这样就不会影响到用户的视觉体验。可以通过CSS预设图片容器的宽高来实现这一点。
  3. 渐进式加载:对于较大的图片,使用渐进式 JPEG 格式可以在图片下载过程中逐步显示,而不是一开始什么都不显示直到完全加载完毕。这样的加载方式虽然不是传统意义上的懒加载,但可以在图片尚未完全下载时提供更好的视觉体验。

对于 JavaScript 和 CSS。JavaScript 可以使用动态导入,CSS 则可以通过 JavaScript 动态创建 <link> 标签,并将其插入到 <head> 中,从而实现 CSS 的懒加载。

ES6 引入的 import() 语法提供了原生方法来实现 JavaScript 代码的动态导入。import() 返回一个 Promise,使其可以与 asyncawait 一起使用,简化异步代码的编写,例如:

1
2
3
4
button.onclick = async () => {
  const module = await import('./myModule.js');
  module.doSomething();
};

另外,Webpack 提供了对懒加载的支持,允许我们指定哪些模块或代码块应该懒加载。