面试知多少?--懒加载与预加载

212 阅读5分钟

对于页面中大量或高质量的图片,如何高效加载?两种常见的优化图片加载性能的策略便是懒加载(Lazy Load)和预加载(Preload)。

一、懒加载(Lazy Load)

1. 懒加载的原理

懒加载是一种延迟加载技术,意味着只有当图片进入可视区域时,才会发起网络请求去加载该图片资源。它的基本思路是:在页面加载时,只加载可视区域内的图片,其他部分的图片则等到用户滚动页面时再加载。这样可以减少初始页面加载的图片数量,从而提升页面加载速度和减少服务器压力。

2. 懒加载的实现

懒加载的实现通常依赖于JavaScript和浏览器的滚动事件。通过简单的滚动事件监听来手动检查图片的可见性。下面是一个简单的懒加载示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>懒加载示例</title>
  <style>
    img {
      height: 300px;
      display: block;
    }
  </style>
</head>
<body>
  <img src="" data-src="https://example.com/image1.gif" alt="">
  <img src="" data-src="https://example.com/image2.gif" alt="">
  <!-- 更多图片 -->

  <script>
    let height = window.innerHeight

    // 判断默认情况下应该展示那些图片
    function lazyLoad() {
      let imgs = document.querySelectorAll('img[data-src]')
      
      for (let i = 0; i < imgs.length; i++) {
        let rect = imgs[i].getBoundingClientRect() // 获取元素的集合属性
        if (rect.bottom > 0 && rect.top < height) {  // 判断元素底部和顶部距离视窗顶部的距离
          // imgs[i].src = imgs[i].getAttribute('data-src')
          
          let newImg = new Image()
          newImg.src = imgs[i].getAttribute('data-src')
          newImg.onload = function () { // 图片被浏览器加载完毕
            imgs[i].src = newImg.getAttribute('src')   //浏览器的img不出现在body里面,只要src有值,也会去发送请求。其他的,比如script,src有值,只要没有出现在body里面,就不会去请求加载
          }
          imgs[i].removeAttribute('data-src')
        }
        
      }
    }
    lazyLoad()

    window.addEventListener('scroll', lazyLoad)
  </script>
</body>
</html>

这里也可以使用IntersectionObserver API来监听元素在视口中的可见度变化从而判断图片是否进入可视区域。

3. 懒加载的优缺点

优点:

  • 提高页面加载速度: 只有可视区域内的图片才会加载,其他图片延迟加载,减少了初始加载的资源消耗。
  • 节省带宽: 用户如果没有滚动到某些图片所在位置,这些图片不会被加载,从而节省带宽和流量。

缺点:

  • 短暂的白屏: 当用户滚动到图片时,可能会有短暂的空白,直到图片加载完成。
  • 影响SEO: 如果搜索引擎爬虫不支持懒加载,可能会影响页面的索引,导致图片资源无法被抓取。

4. 适用场景

  • 图片较多的页面: 适合那些包含大量图片或大尺寸图片的页面,比如商品展示页、图片库、社交媒体动态等。
  • 优化移动端: 在移动端,用户通常网络速度较慢,懒加载能显著提升体验。

二、预加载(Preload)

1. 预加载的原理

预加载是一种提前加载图片资源的策略,它会在页面加载时,利用额外的线程去加载一些用户可能会查看的图片。当用户滚动到这些图片时,它们已经在浏览器缓存中,从而快速展示。预加载可以通过JavaScript的Worker线程和XMLHttpRequest来实现,减少主线程的阻塞。

2. 预加载的实现

预加载的核心思想是创建一个新的线程,利用这个线程提前请求图片资源。在图片加载完毕后,将图片资源交给主线程来显示。以下是一个预加载的实现示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>预加载示例</title>
</head>
<body>
  <div id="pic"></div>

  <script>
    let pic = document.getElementById('pic');
    let arr = [
      "https://example.com/image1.gif",
      "https://example.com/image2.gif",
      "https://example.com/image3.gif",
      // 更多在线图片链接
    ];

    // 创建一个新的线程
    const worker = new Worker('worker.js')
    // 将数据发送给子线程
    worker.postMessage(arr)
    // 接收子线程发送的数据
    worker.onmessage = function (e) {
      console.log(e.data);
      const img = new Image()
      // console.log(window.URL.createObjectURL(e.data));
      img.src = window.URL.createObjectURL(e.data)
      pic.appendChild(img)  //预加载图片加载不是按顺序,先请求到先展示(可优化)
    }
  </script>
</body>
</html>

在分线程worker.js中,代码会如下所示:

self.onmessage = function(e) {
  // console.log(e.data);
  // 将数组中的地址资源加载出来
  let arr = e.data;
  for (let i = 0; i < arr.length; i++) {
    let xhr = new XMLHttpRequest();
    xhr.open("get", arr[i], true);
    xhr.responseType = 'blob'  // 返回blob文件类型
    xhr.send();
    xhr.onload = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        // console.log(xhr.response);
        self.postMessage(xhr.response); // 返回blob文件类型
      }

    }
  }
}
// 非主线程不能用 new Image()

此时预加载完成,前端会向本地请求图片资源。

image.png

image.png

3. 预加载的优缺点

优点:

  • 避免滚动时的白屏: 由于图片资源在后台加载完成,用户滚动到相应位置时不会看到加载中的空白。

缺点:

  • 增加服务器压力: 在页面加载时就请求了所有图片,可能导致短时间内对服务器的请求量激增,尤其是当图片资源非常多时。
  • 浪费带宽: 预加载的图片可能是用户最终不会查看的,浪费带宽和流量。

4. 适用场景

  • 图片数量有限且较小的页面: 适合那些图片数量不多且大小较小的页面,预加载可以提高体验而不会给服务器带来过大压力。

三、懒加载与预加载的比较

特性懒加载预加载
加载时机图片进入可视区域时加载页面加载时即开始加载所有图片
性能影响减少初始加载时间,节省带宽可能增加页面加载时间和服务器压力
用户体验图片过大时,滚动时可能有短暂的白屏用户滚动时图片立即显示,无延迟或空白区域 ,但首屏加载可能出现稍长时间白屏