前端优化之图片懒加载

0 阅读5分钟

在当今的网页开发中,性能优化是至关重要的一环。特别是在电商等图片密集型的网页中,大量图片的加载会严重影响页面的打开速度和用户体验。图片懒加载技术应运而生,它可以显著提升页面的首屏加载速度,减少不必要的网络请求。本文将深入探讨图片懒加载的原理、实现方式以及优化过程。

图片懒加载的基本原理

在传统的网页中,当浏览器解析到 <img> 标签的 src 属性时,会立即发起 HTTP 请求去下载对应的图片。如果页面中有大量图片,同时发起多个请求会导致网络带宽被占满,页面加载速度变慢,甚至可能出现无法打开的情况。

图片懒加载的核心思想是只加载当前可视区域内的图片,当用户滚动页面时,再动态加载即将进入可视区域的图片。这样可以减少首屏加载时的图片数量,提高页面的加载速度。

实现懒加载的关键步骤

  1. 占位图:在 <img> 标签的 src 属性中设置一个小尺寸的占位图,避免一开始就请求原图片地址,减少并发请求和图片大小对性能的影响。占位图通常只需要请求一次,会被浏览器缓存。
  2. 自定义属性:使用自定义属性 data-original 来存储图片的原地址。这样可以在需要加载图片时,从 data-original 中获取原地址并赋值给 src 属性。
  3. 监听滚动事件:通过监听页面的滚动事件,判断图片是否进入可视区域。当图片进入可视区域时,将 data-original 的值赋给 src 属性,触发图片的加载。

基础实现:index.html

下面是 index.html 中的代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片懒加载</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    .image-item{
        width: 350px;
        height: 350px;
        margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <img  class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif" data-original="https://img.36krcdn.com/hsossms/20250313/v2_15ad8ef9eca34830b4a2e081bbc7f57a@000000_oswg172644oswg1536oswg722_img_000?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center" />
  <!-- 更多图片... -->
<script>
    const viewHeight = document.documentElement.clientHeight;
    const eles = document.querySelectorAll('img[data-original][lazyload]');
    
    const lazyload = function () {
        Array.prototype.forEach.call(eles,function(item,index){
            if(item.dataset.original === "") return;
            rect = item.getBoundingClientRect();
            if(rect.bottom >= 0 && rect.top <= viewHeight){
                (function(){
                    var img = new Image();
                    img.src = item.dataset.original;
                    img.onload = function(){
                        item.src = item.dataset.original;
                        item.removeAttribute('data-original');
                        item.removeAttribute('lazyload');
                    }
                })()
            }
        })
    }
    window.addEventListener('scroll', lazyload);
    document.addEventListener('DOMContentLoaded', lazyload);

</script>
</body>
</html>

代码解释

  1. 获取可视区域高度const viewHeight = document.documentElement.clientHeight; 获取当前浏览器可视区域的高度。
  2. 选择需要懒加载的图片const eles = document.querySelectorAll('img[data-original][lazyload]'); 选择所有带有 data-originallazyload 属性的 <img> 标签。
  3. 懒加载函数lazyload 函数遍历所有需要懒加载的图片,使用 getBoundingClientRect() 方法获取图片在视口的位置。如果图片的底部大于等于 0 且顶部小于等于可视区域高度,则表示图片进入了可视区域,此时创建一个新的 Image 对象,将 data-original 的值赋给 src 属性。当图片加载完成后,将 src 属性更新为原地址,并移除 data-originallazyload 属性。
  4. 监听事件:通过 window.addEventListener('scroll', lazyload); 监听页面的滚动事件,当页面滚动时触发 lazyload 函数。同时,通过 document.addEventListener('DOMContentLoaded', lazyload); 在页面渲染完成后立即执行一次 lazyload 函数,确保首屏的图片能够正常加载。

性能问题

虽然这种实现方式可以实现图片懒加载的基本功能,但存在一些性能问题:

  • onScroll 触发太频繁:当用户滚动页面时,onScroll 事件会频繁触发,导致 lazyload 函数被多次调用,增加了浏览器的负担。
  • forEach 遍历:每次滚动都需要遍历所有需要懒加载的图片,即使有些图片已经加载完成,也会被重复检查,浪费了性能。
  • getBoundingClientRect 触发回流getBoundingClientRect() 方法会触发浏览器的回流操作,频繁调用会影响页面的性能。

优化实现:observer.html

为了解决上述性能问题,可以使用 IntersectionObserver API 来实现图片懒加载。IntersectionObserver 是一个异步的 API,它可以在目标元素与视口或指定的根元素交叉时触发回调函数,避免了频繁的滚动事件监听和回流操作。

下面是 observer.html 中的代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .list-item{
                height: 400px;
                margin: 5px;
                list-style: none;
        }
        .list-item-img{
                height: 100%;
        }
    </style>
</head>
<body>
    <ul id="list">
        <li class="list-item"><img src="" class="list-item-img" lazyload="true" data-original="https://img.36krcdn.com/hsossms/20250313/v2_15ad8ef9eca34830b4a2e081bbc7f57a@000000_oswg172644oswg1536oswg722_img_000?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center" /></li>
        <!-- 更多图片... -->
    </ul>
    <script>
        function addObserver(){
            const eles = document.querySelectorAll('img[data-original][lazyload]');

            const observer = new IntersectionObserver(function(changes){
                    changes.forEach(function(element){
                        if(element.intersectionRatio > 0 && element.intersectionRatio <= 1){
                                const img = new Image();
                                img.src = element.target.dataset.original;
                                img.onload = function(){
                                    element.target.src = element.target.dataset.original;
                                    element.target.removeAttribute('data-original');
                                    element.target.removeAttribute('lazyload');
                                }
                        }
                    })
            })
            eles.forEach(ele=>{
                observer.observe(ele);
            })
        }
        addObserver();
    </script>
</body>
</html>

代码解释

  1. 选择需要懒加载的图片const eles = document.querySelectorAll('img[data-original][lazyload]'); 选择所有带有 data-originallazyload 属性的 <img> 标签。
  2. 创建 IntersectionObserver 实例const observer = new IntersectionObserver(callback); 创建一个 IntersectionObserver 实例,并传入一个回调函数。回调函数会在目标元素与视口交叉时被调用,接收一个 changes 数组作为参数,数组中的每个元素代表一个目标元素的交叉变化。
  3. 处理交叉变化:在回调函数中,遍历 changes 数组,通过 element.intersectionRatio 判断目标元素与视口的交叉比例。如果交叉比例大于 0 且小于等于 1,则表示目标元素进入了视口,此时创建一个新的 Image 对象,将 data-original 的值赋给 src 属性。当图片加载完成后,将 src 属性更新为原地址,并移除 data-originallazyload 属性。
  4. 观察目标元素eles.forEach(ele=>{ observer.observe(ele); }); 遍历所有需要懒加载的图片,使用 observer.observe() 方法将它们添加到观察列表中。

优化效果

使用 IntersectionObserver API 实现图片懒加载的主要优化效果如下:

  • 减少 onScroll 次数IntersectionObserver 是异步的,在浏览器的后台运行,不需要监听 onScroll 事件,避免了频繁的滚动事件触发,减少了浏览器的负担。
  • 避免回流IntersectionObserver 会自动监测目标元素与视口的交叉情况,不需要手动调用 getBoundingClientRect() 方法,避免了回流操作,提高了页面的性能。

总结

图片懒加载是一种有效的性能优化技术,可以显著提升页面的首屏加载速度和用户体验。通过使用占位图、自定义属性和监听滚动事件,可以实现基本的图片懒加载功能。但为了进一步优化性能,建议使用 IntersectionObserver API 来实现,它可以避免频繁的滚动事件监听和回流操作,提高页面的性能和响应速度。