每日一问 : 如何实现图片懒加载

558 阅读4分钟

前言

在需要使用大量图片时,如果我们打开页面时就加载所有的图片,那势必会导致页面的卡顿以及白屏,给用户不好的体验,导致用户流失。

但是我们仔细想一下,用户真的需要我们显示所有图片一起展示吗?其实并不是,用户看到的只是浏览器可视区域的内容。所以从这个情况我们可以做一些优化,只显示用户可视区域内的图片,当用户触发滚动的瞬间再去请求显示给用户, 所以图片懒加载的有势在必行。

图片懒加载的优点

  1. 减小资源的加载,页面启动只加载首屏的图片,这样能明显减少服务器的压力和流量,提高页面的加载速度,减少浏览器的负担

  2. 防止并发加载的资源过多而阻塞js加载,影响整个网站的启动

  3. 能提升用户的体验,不妨设想下,用户打开页面的时候,如果页面上所有的图片都需要加载,由于图片数目较大,等待时间很长这就严重影响用户体验

实现原理

图片的加载是由src的值引起的,当对src赋值时,浏览器会请求图片资源,基于这个,可以利用html5的属性data-xxx来保存图片的路径,当我们需要加载图片的时候才将data-xxx的值赋予src,就能实现突破的按需加载,也就是懒加载了

懒加载的基本思路

当dom元素进入可视区域时,才去加载它。

如何判断一个dom元素是否进入了可见区域?

实现思路

利用浏览器提供的 IntersectionObserver,监听图片元素是否进入可视区域,进入后才真正去设置图片元素的 src 属性进行图片加载

IntersectionObserver 使用方法

IntersectionObserver() 构造器创建并返回一个IntersectionObserver对象。 如果指定rootMargin则会检查其是否符合语法规定,检查阈值以确保全部在0.0到1.0之间,并且阈值列表会按升序排列。如果阈值列表为空,则默认为一个[0.0]的数组

语法 :

var observer = new IntersectionObserver(callback[, options]);

参数:

  • callback

  • 当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:

  • options 可选

  • 一个可以用来配置observer实例的对象。如果options未指定,observer实例默认使用文档视口作为root,并且没有margin,阈值为0%(意味着即使一像素的改变都会触发回调函数)。你可以指定以下配置:

    • root

      监听元素的祖先元素Element对象,其边界盒将被视作视口。目标在根的可见区域的的任何不可见部分都会被视为不可见。

    • rootMargin

      一个在计算交叉值时添加至根的边界盒(bounding_box (en-US))中的一组偏移量,类型为字符串(string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS 中的margin 属性等同; 可以参考 The root element and root margin in Intersection Observer API来深入了解margin的工作原理及其语法。默认值是"0px 0px 0px 0px"。

    • threshold

    规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组0.0到1.0之间的数组。若指定值为0.0,则意味着监听元素即使与根有1像素交叉,此元素也会被视为可见. 若指定值为1.0,则意味着整个元素都在可见范围内时才算可见。可以参考Thresholds in Intersection Observer API 来深入了解阈值是如何使用的。阈值的默认值为0.0。

     var dom = dom元素
    // 实例化一个观察者
    // 它的参数1是一个回调:当被观察的目标进入视口/离开视口就会调用
    var observer = new IntersectionObserver((entries)=>{
      console.log(entries[0].isIntersecting)
      console.log(entries[0].intersectionRatio)
      if(entries[0].isIntersecting) {
    
      }
    }, 其他配置)
    
    // 观察者观察dom
    observer.observe(dom)
    observer.disconnect()   // 停止观察者
    observer.unobserve(dom) // 观察者停止对dom的观察
    

栗子:

<!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>
    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: skyblue;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <img data-src="https://tse2-mm.cn.bing.net/th/id/OIP-C.P3NSGTdAYdyqy5zJpb5QXQHaEo?pid=ImgDet&rs=1" alt="">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <script>
        const img = document.querySelector('img')
        const observer = new IntersectionObserver(arr => {
            console.log(arr[0].isIntersecting);
            console.log(arr[0].intersectionRatio);
            if (arr[0].isIntersecting) {
                img.src = img.getAttribute('data-src')
                img.onerror = function () {
                    img.src = 'https://tse4-mm.cn.bing.net/th/id/OIP-C.Yu52PeiUicZhrQgZFa-qTgAAAA?pid=ImgDet&rs=1'
                }
            }
        }, { rootMargin: "100px" })
        observer.observe(img)
    </script>
</body>

</html>

效果:

滚动前: 不发请求获取图片资源

image.png

达到一定的视口距离: 获取图片资源

image.png

当图片加载失败 : 这里给的是错误的图片路径

<img data-src="https://tse25-mm.cn.bing.net/th/id/OIP-C.P3NSGTdAYdyqy5zJpb5QXQHaEo?pid=ImgDet&rs=1" alt="">

效果 : 加载图片资源失败 ,替换为失败默认图片

image.png

总结 :

  1. 为提高网站加载性能,图片懒加载是必要的。

  2. 图片懒加载是实现原理是判断当前图片是否到了可视区域进行加载,可通过监听 scroll 事件和 IntersectionObserver 实现相应的功能