一个图片懒加载的demo

223 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

问题

图片懒加载是一个非常常见的前端优化技巧。其基本逻辑是:

  1. 图片初始不设置src属性值。这样它就不会请求这个图片资源。
  2. 实时监听图片是否进入了视口区域,如果进入了,就设置它的src属性,并取消实时监听。

上面的第2点对应的伪代码是:

window.滚动条事件 = () => {
    if(图片进入可视区) {
       图片.src = 图片地址
       
       取消监听
    }
}

代码实现有两个难点:

  1. 如何判断图片是否进入可视区?
  2. 如何取消监听?

好了,今天介绍一个老朋友:IntersectionObserver

IntersectionObserver的格式

// 0. 这是要观察的dom
var dom = dom元素

// 1. 实例化一个观察者
// 参数1: 是一个回调,当被观察的目标进入视口或者离开视口时会被调用
// 参数2:[配置项](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/IntersectionObserver#%E5%8F%82%E6%95%B0)
var observer = new IntersectionObserver((entries)=>{
  console.log(entries)
}, 配置)

// 2. 开启观察dom
observer.observe(dom) 
// 此时,滚动条的滚动 会触发上边的回调函数。

// 3. 其他辅助api
observer.disconnect()   // 停止观察者行为
observer.unobserve(dom) // 停止对dom的观察

图片懒加载的参考demo

下面的代码直接复制就可以跑起来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    <div>
        <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p>
        <img height="200" data-src="https://gimg2.baidu.com/image_search1/src=http%3A%2F%2Ffzn.cc%2Fwp-content%2Fuploads%2F2020%2F04%2F640-8.jpg&refer=http%3A%2F%2Ffzn.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641365183&t=b5d7bdae0fe3f2c4831b52e3985abdf1" />
        <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p>
    </div>

    <script>
        // 1. 找到dom
        var img = document.querySelector("img")
        // 2. 实例化 IntersectionObserver对象
        var observer = new IntersectionObserver(arr=>{
            console.log(arr[0].isIntersecting) // 是否进入;false | true
            if(arr[0].isIntersecting) {
                img.src = img.getAttribute('data-src')
                img.onerror = function(){
                    console.log('图片加载失败....')
                    img.src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fcdb58e6860bcf06028c4b40e47aa17fd7ffa2e6f67cc-UQZRFK_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641366439&t=07c8d22d1c35def62dcaaf04d94e1255"
                }
                // 不再监听了
                observer.unobserve(img)
            }
        }, {rootMargin: "100px"}) // 

        // 开始监听
        observer.observe(img)
    </script>
</body>
</html>

效果

初始图片并不可见,快了进入视口区域才可见(注意上面的rootMargin:"100px")

持续改进

  1. 封装成组件。根据具体的技术栈来做封装。
  2. 处理异常。 图片加载失败(例如地址错误)
  3. 优化体验。例如可以提供加载中,加载失败的占位图