最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 彻底玩转图片懒加载及底层实现原理

    正文概述 掘金(前端森林)   2021-02-03   505

    前言

    图片懒加载其实已经是一个近乎“烂大街”的词语了,在大大小小的面试中也会被频繁的问到,我在之前的面试中也被问到了图片懒加载的原因、实现方式及底层原理,但由于自己平时很少做“图片”相关的处理,对于“懒加载”也是知之甚少,所以在面试中问答的也不是很好。

    今天,我将首先从浏览器底层渲染机制来剖析为什么要去做图片懒加载,之后我将带大家一起来看下目前主流的几种实现图片懒加载的方式及其实现原理,最后会做一个展望。

    为什么要做图片懒加载

    要问答这个问题,首先我们先来看下浏览器的底层渲染机制:

    1、构建 DOM 树

    2、样式计算

    3、布局阶段

    4、分层

    5、绘制

    6、分块

    7、光栅化

    8、合成

    而在构建DOM的过程中如果遇到img在新老版本的chrome中表现又是不一样的:

    • 老版本:阻塞 DOM 渲染
    • 新版本:虽然不会阻塞 DOM 渲染,但每一个图片请求都会占用一个 HTTP,而且 Chrome 最多允许对同一个 Host 同时建立六个 TCP 连接

    当你打开一个网站时,浏览器会做许多工作,这其中包括下载各种可能用到的资源,然后渲染呈现在你面前,假设你的网站有大量的图片,那么加载的过程是很耗时的,尤其像那些电商类需要大量图片的网站,可想而知,网站的初始加载时间会很长,再加上网络等其它影响,用户体验会很差。

    相信你经常遇到过一个网站卡在某个地方,一直在加载,这种体验很不好。我们都希望一输入网址,页面立马就呈现在眼前。

    总结一下就是:直接全部加载的话会减缓渲染速度,产生白屏等进而影响用户体验

    基于原生 js 实现图片懒加载

    相关 API

    先来看几个后面会用到的API

    document.documentElement.clientHeight

    获取屏幕可视区域的高度。

    彻底玩转图片懒加载及底层实现原理

    element.offsetTop

    获取元素相对于文档顶部的高度。

    彻底玩转图片懒加载及底层实现原理

    document.documentElement.scrollTop

    获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离。

    彻底玩转图片懒加载及底层实现原理

    思路分析

    通过上面三个 API,我们获得了三个值:可视区域的高度、元素相对于其父元素容器顶部的距离、浏览器窗口顶部与容器元素顶部的距离也就是滚动条滚动的高度。

    虽然这几个API很简单,但是单纯的去说还是有点抽象,这里我们还是用图来展示一下: 彻底玩转图片懒加载及底层实现原理

    看完上面这张图片,我想你已经明白了:如果满足offsetTop-scroolTop<clientHeight,则图片进入了可视区内,我们就去请求进入可视区域的图片。

    代码实现

    基于上面的分析,我们很容易就可以写出如下代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>基于原生 js 实现图片懒加载</title>
        <style>
            img {
                display: block;
                width: 100%;
                height: 300px;
                margin-bottom: 20px;
            }
        </style>
    </head>
    <body>
        <img data-src="./img/1.png" >
        <img data-src="./img/2.png" >
        <img data-src="./img/3.png" >
        <img data-src="./img/4.png" >
        <img data-src="./img/5.png" >
        <img data-src="./img/6.png" >
        <img data-src="./img/7.png" >
        <img data-src="./img/8.png" >
    </body>
    <script>
            var imgs = document.querySelectorAll('img');
    
            //offsetTop是元素与offsetParent的距离,循环获取直到页面顶部
            function getRealTop(e) {
                var realTop = e.offsetTop;
                while(e = e.offsetParent) {
                    realTop += e.offsetTop;
                }
                return realTop;
            }
    
            function lazyLoad(imgs) {
                var H = document.documentElement.clientHeight;//获取可视区域高度
                var S = document.documentElement.scrollTop || document.body.scrollTop;
                for (var i = 0; i < imgs.length; i++) {
                    if (H + S > getRealTop(imgs[i])) {
                        imgs[i].src = imgs[i].getAttribute('data-src');
                    }
                }
            }
    
            window.onload = window.onscroll = function () { //onscroll()在滚动条滚动的时候触发
                lazyLoad(imgs);
            }
    </script>
    </html>
    

    基于 getBoundingClientRect()实现图片懒加载

    先来了解一下这个API吧:

    getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。getBoundingClientRect()DOM元素到浏览器可视范围的距离(不包含页面看不见的部分)。

    该函数返回一个rectObject对象,该对象有 6 个属性:top, left, bottom, right, width, height;这里的topleftcss中的理解很相似,widthheight是元素自身的宽高,但是rightbottomcss中的理解有点不一样。right是指元素右边界距窗口最左边的距离,bottom是指元素下边界距窗口最上面的距离。

    彻底玩转图片懒加载及底层实现原理

    思路分析

    通过这个 API,我们就很容易获取img元素相对于视口的顶点位置rectObject.top,只要这个值小于浏览器的高度window.innerHeight就说明进入可视区域:

    function isInSight(el){
      const bound = el.getBoundingClientRect();
      const clientHeight = window.innerHeight;
      return bound.top <= clientHeight;
    }
    

    代码实现

    这里结合第一种实现方式,做下改造,就得到了:

    function loadImg(el){
     if(!el.src){
       const source = el.getAttribute('data-src');;
       el.src = source;
     }
    }
    function checkImgs(){
      const imgs = document.querySelectorAll('img');
      Array.from(imgs).forEach(el =>{
        if (isInSight(el)){
          loadImg(el);
        }
      })
    }
    window.onload = function(){
      checkImgs();
    }
    document.onscroll = function () {
      checkImgs();
    }
    

    基于 IntersectionObserver 实现图片懒加载

    概念

    同样,还是先来看一下概念。

    我们在平时的开发中,常常需要了解某个元素是否进入了"视口"(viewport),即用户能不能看到它。

    彻底玩转图片懒加载及底层实现原理

    上图的绿色方块不断滚动,顶部会提示它的可见性。

    传统的实现方法是,监听到scroll事件后,调用目标元素(绿色方块)的getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件密集发生,计算量很大,容易造成性能问题。

    目前有一个新的 IntersectionObserver API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做交叉观察器

    使用

    它的用法也非常简单。

    var io = new IntersectionObserver(callback, option);
    

    上面代码中,IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。

    构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点。

    // 开始观察
    io.observe(document.getElementById('container'));
    
    // 停止观察
    io.unobserve(element);
    
    // 关闭观察器
    io.disconnect();
    

    上面代码中,observe的参数是一个 DOM 节点对象。

    如果要观察多个节点,就要多次调用这个方法。

    io.observe(elementA);
    io.observe(elementB);
    

    代码实现

    看完相关的API,下面就让我们基于IntersectionObserver来实现图片懒加载:

    const imgs = document.querySelectorAll('img') //获取所有待观察的目标元素
    var options = {}
    function lazyLoad(target) {
      const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entrie => {
          if (entrie.isIntersecting) {
            const img = entrie.target;
            const src = img.getAttribute('data-src');
            img.setAttribute('src', src)
            observer.unobserve(img); // 停止监听已开始加载的图片
          }
    
        })
      }, options);
      observer.observe(target)
    }
    
    imgs.forEach(lazyLoad)
    

    img.loading=lazy

    最后这种相对就简单很多了,它是 Chrome 自带的原生 lazyload 属性。我们先来看下他在各大浏览器的支持程度:

    彻底玩转图片懒加载及底层实现原理

    它的使用也非常简单,如标题所示:

    <img src="example.jpg" loading="lazy"  width="250" height="150">
    

    关于原生懒加载 loading=”lazy”的更多介绍可以参考张鑫旭大佬的浏览器 IMG 图片原生懒加载 loading=”lazy”实践指南。


    起源地下载网 » 彻底玩转图片懒加载及底层实现原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元