渲染性能优化---懒加载

312 阅读3分钟

一、背景介绍

在实际项目中,可能会存在大量的图片资源,如果一开始就完全加载所有的图片,可能会导致页面加载缓慢,用户体验较差。这时,可以采用一种叫做懒加载(Lazy load)的技术,具体做法是:当图片滚动至用户可视范围内时,再去加载真正的图片资源。

懒加载技术所带来的好处如下:

  1. 减少首屏加载资源,提高页面加载速度;
  2. 减轻服务器资源消耗,节省带宽流量;
  3. 可有效解决浏览器限制的最大并发请求数量。

本文将详细介绍前端图片懒加载技术的实现原理,以及结合实际例子实现懒加载功能。

二、实现原理

图片懒加载的关键点在于图片资源的加载时机,即,当图片出现在用户的视口范围内时,才去请求实际的图片资源。监听图片是否滚动到视口范围内,可以通过以下两种方式实现:

  1. 监听页面滚动事件scroll,并结合图片相对视口的位置来判断图片是否出现在视口范围内;
  2. 使用IntersectionObserverAPI实现,当目标元素与父元素相交时,触发回调函数。

下面将分别介绍这两种实现方式。

2.1 监听滚动事件判断图片位置

(1)HTML结构

首先,我们需要为图片占位,并提供一个占位符(如:默认图片或者一张透明的1px*1px的图片),在图片懒加载完成之前展示。同时,我们需要将图片的真实URL保存在data-src属性中。

<img class="image-lazy" data-src="http://example.com/real/image/url" src="http://example.com/placeholder/url">

(2)JavaScript实现

首先,我们需要编写一个判断元素是否在可视范围内的函数:

function isElementInView(element) {
    const rect = element.getBoundingClientRect();
    return (
        rect.top = (window.innerHeight || document.documentElement.clientHeight)
        rect.left = (window.innerWidth || document.documentElement.clientWidth)
    );
}

接下来,编写一个加载图片的函数。当判断到某个图片元素在可视范围内时,就将该图片的data-src属性赋值给src属性,同时移除data-src属性:

function loadImage(imgEl) {
    const realSrc = imgEl.getAttribute("data-src");
    if (realSrc) {
        imgEl.src = realSrc;
        imgEl.removeAttribute("data-src");
    }
}

最后,我们需要监听页面滚动事件,并判断懒加载图片是否处于可视范围内:

window.addEventListener("scroll", function() {
    const imgs = document.querySelectorAll(".image-lazy[data-src]");
    imgs.forEach((img) => {
        if (isElementInView(img)) {
            loadImage(img);
        }
    });
});

优缺点:

优点是实现较为简单,兼容性较好;缺点是会产生性能问题,因为页面滚动时触发频率极高,产生大量重绘、重排操作。

为了解决该问题,可以使用requestAnimationFramesetTimeout或者防抖节流优化滚动事件。这里不再赘述。

2.2 使用IntersectionObserver API实现

IntersectionObserverAPI是现代浏览器提供的一种通用的观察器模式实现,用于监听元素进入或离开另一个元素(如视口)。

(1)HTML结构

与监听滚动事件的实现方式相同,

<img class="image-lazy" data-src="http://example.com/real/image/url" src="http://example.com/placeholder/url">

(2)JavaScript实现

首先,创建一个IntersectionObserver实例:

const observer = new IntersectionObserver(function(entries) {

   // 对每个相交的图片元素,处理懒加载
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            loadImage(entry.target);
            // 当图片懒加载完成,取消对该图片的观察
            observer.unobserve(entry.target);
        }
    });

}, {
    rootMargin: '100px', // 兼容用户提前滚动浏览到图片的场景
    threshold: 0
});

在此基础上,我们需要使用IntersectionObserver观察页面上的所有懒加载图片:

const lazyImages = document.querySelectorAll('.image-lazy');
lazyImages.forEach(img => observer.observe(img));

优缺点:

优点是性能较好,加载逻辑与滚动事件解耦,无需额外优化;但缺点是兼容性较差,需要使用polyfill解决。

三、第三方库

除了自己实现懒加载功能,还可以使用现有的成熟的第三方库,如:lazysizeslozad等。

在实际项目中可以根据需要选择不同的技术方案。在兼容性要求不高的情况下,可以优先选择IntersectionObserverAPI实现。