利用Intersection Observer实现图片懒加载性能优化

Intersection Observer是浏览器所提供的一个 Javascript API,用于异步的检测目标元素以及祖先或者是顶级的文档视窗的交叉状态

这句话的意思就是:

我们可以看的图片当中,绿色的 target element(目标元素),并且存在一个顶层的或者祖先的文档视窗也就是当前图片中的,灰色的 browser viewport(浏览器的视窗)

当 target element(目标元素)进行移动的时候,将会与 browser viewport(浏览器的视窗)进行交叉状态的监控,那么利用这个交叉状态的监控,我们就可以实现诸如一个懒加载无限滚动或者是与元素可见性相关的一种操作。

Intersection Observer基本概念的理解:

Intersection Observer是一个观察期,创建一个观察的对象,该对象可以观察一个或多个元素,而我们的目标元素target element 则是需要被观察的 dom元素,至于 intersection ratio 也就是交叉 其目标元素 与 其祖先或视窗 相交的一个状态,那么交叉比例也就是 intersection ratio指的是目标元素与其视窗或祖先元素相交的一个百分比。我们可以从上方图片中感受到这里面如果绿色的顶部这条线和 browser viewport 的底部是重合的,我们的交叉比例应该是0;如果是图片中当前的位置,我们的交叉比例应该接近于0.5也就是百分之五十;如果在往上走的时候,我们的交叉比例可能就是1

那么利用Intersection Observer能否去实现懒加载的性能优化呢,我们需要先确认的是:为什么会需要进行性能优化、我们原来进行图片懒加载的方式又是怎样的,我们可以通过一个实例进行相应是说明:

html 代码:

利用link进行相对应的样式引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./index2.css">
</head>
<body>
    <div>
        <h1>图片相册</h1>
        <img data-src="//i2.wp.com/pic1.win4000.com/wallpaper/2018-09-19/5ba21a3006800.jpg" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=1785207335,3397162108&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=338595665,4065109605&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=1732966997,2981886582&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=2581522032,2615939966&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=245883932,1750720125&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=3423293041,3900166648&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=3241434606,2550606435&fm=193&f=GIF" alt=""> 
        <img data-src="//i2.wp.com/t7.baidu.com/it/u=1417505637,1247476664&fm=193&f=GIF" alt="">
        <img data-src="//i2.wp.com/img0.baidu.com/it/u=775184654,1087701200&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500">
        <img data-src="//i2.wp.com/lmg.jj20.com/up/allimg/1114/1110200ZS0/2011100ZS0-9-1200.jpg" alt="">
    </div>
    
</body>
<script src="./index2.js"></script>
</html>

css代码:

// 对页面中的img标签进行一个控制
img{
    width: 100vw;  // 宽度
    transform: translateX(50%);  // 横轴变化的操作(也就是位移)
    opacity: 0;  // 不可见
    transition: all 500ms;  // 动画的控制
} 
 
.fade{
    transform: translateX(0);  // 位移操作
    opacity: 1;  // 可见状态
    transition: all 500ms;  // 动画的操作
}

js代码:

//查询所有的img标签内容
const targets = document.querySelectorAll('img');

// 监听滚动事件
window.addEventListener("scroll", (event) => {
    // 遍历所有的img标签
    targets.forEach((img) => {
        // 获取img标签的top值
        console.log("load img")
        const rect = img.getBoundingClientRect().top;
        // 如果img标签的top值小于窗口的高度
        if (rect <= window.innerHeight){
            // 获取img标签的data-src属性值
            const src = img.getAttribute("data-src");
            // 将data-src属性值赋值给src属性
            img.getAttribute("src", src);
            // 添加fade类名
            img.classList.add("fade");
        }
    })
})

我们可以查看一下页面:

当我们进行滚动操作的时候,将会不断的加载显示我们所需要显示的应一个图片,但是,当我们将控制台切换到console,会发现当我们滚动滚动条时,打印的信息频率异常的高,随随便便就产生了上千次的打印代码的输出操作,这也就是意味着我们的性能其实是极其的低下的,那么如何去减少滚动时对于图片懒加载显示的性能提升呢?

我们可以去考虑,当这个图片显示在这个browser viewport(浏览器的视窗),我们对其进行的是观察操作,只有在可视区里面,我们才对图片进行处理,利用的就是Intersection Observer的操作处理。

那么利用Intersection Observer进行具体功能的实现:

//查询所有的img标签内容
const targets = document.querySelectorAll('img');

// 定义一个函数
const lazyload = target =>{
    // 进行实体对象entries,并且设置一个observer参数内容
    const io = new IntersectionObserver((entries,observer) =>{
        // 进行实体对象的循环(这个实体对象便是我们之后的图片)
        entries.forEach((entry)=>{  // entry:每一个实体对象
            console.log("load img")
            
            // 当图片实例内容进行到我们的观察区时(也就是交叉的一种状态)
            if(entry.isIntersecting){ 
                const img = entry.target  // 设置一个图片,便是target目标
                const src = img.getAttribute("data-src")  // 通过data-src这个自定义数据内容进行来源数据的获取属性
                img.setAttribute("src",src);  // 设置图片路径
                img.classList.add("fade")  // 添加动画效果
                observer.disconnect();  // 将观察的内容进行取消连接的一个状态
            }
        })
    })
    io.observe(target)  // 通过io进行具体的observe观察图片内容
}
 
targets.forEach(lazyload);  // 最终对图片(获取到的图片数组)进行循环,调用的则是lazyload 自定义函数

此时,我们再进行滚动下来操作时,console打印的次数只有十几次:

而使用scroll监听滚动时,有成千次的执行,所以我们可以利用Intersection Observer进行一个图片懒加载性能优化的一个处理。