有的时候懒一点不也挺好的嘛

560 阅读6分钟

在现实生活中,好多人往往都会说,勤快一点好勤快一点好,可是懒一点他真的不好吗?那可不一定,就比如在前端开发中,我们更需要去率先的展示我们的首页,也就是只加载其中的一部分,而其他部分就先懒着,等需要的是再去加载。而在现代网页设计中,图片是视觉和信息传达的关键元素。然而,在移动设备和低带宽网络环境下,大量图片的加载可能拖慢页面加载速度。图片懒加载技术通过延迟非可视区域内的图片加载,提高了首屏加载速度和用户体验。

什么是图片懒加载

图片懒加载(Lazy Loading)是一种网页优化技术,用于改善网站性能和用户体验。其核心思想是延迟加载页面中某些元素(通常是图片或视频)直到它们出现在用户的可视区域内,或者直到用户滚动到它们所在的位置时才开始加载。这样做的主要目的是减少初始页面加载时间,让页面更快的显示出来,提高页面的响应速度和整体性能。

浏览器的工作机制

  • 页面解析与渲染:浏览器下载HTML文档,构建DOM树,并下载CSS样式表以创建CSSOM树。之后,DOM树与CSSOM树结合生成渲染树,然后计算每个元素的大小和位置生成布局树,根据布局树绘制图层,然后将图层和平在一起,最终呈现页面。# 浏览器内核与渲染机制深入解读(一)
  • 多线程与资源下载:尽管JavaScript执行在单线程中,但浏览器能够使用多线程处理图片、链接和脚本的下载。而浏览器为了让页面快速显示,会并发多线程,而过多的并发请求可能导致网络拥堵,影响加载效率。
  • 真的有必要并发吗:为了用户的体验,浏览器都会尽快的将首页页面显示出来,但是如果首页里面都是图片或者其他链接什么的,真的有必要全部显示吗?我们都知道购物软件中存在多张商品的图片,我们总不能在每个用户访问的时候都将所有图片一股脑的全部给出吧,毕竟在618等购物节日中是由大量的用户去访问,那这浏览器服务器不得冒烟????

优化策略

  • 首屏图片优先加载:确保用户看到关键内容,提高首屏加载速度。
  • 滚动时动态加载:仅在图片即将进入可视区域时开始下载,减少初始加载时间。

原理

实现图片懒加载的关键在于判断图片是否进入视口。通常通过以下方式实现:

  1. 获取元素相对于页面顶部的位置和视口的大小。

  2. 计算图片元素与视口的关系,判断是否可见。

  3. 根据查找,我们需要使用clientHeightscrollTopoffsetTop等属性判断图片是否进入可视区域。其中clientHeight 是 可视区域高度也就是一屏之高、 scrollTop 则是滚动条当前位置也可以说是滚动条的高度、 而 offsetTop 则是获取元素距离顶部的距离

实现步骤

  1. 为需要懒加载的图片添加自定义属性,将图片的src属性替换为data-src,原生的src属性用于立即下载图片,而data-src则作为图片的真实来源

    <img  data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567642423719_img_000">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567642425030_img_000">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567642425101_img_000">
    <img data-src="https://img.36krcdn.com/20190905/v2_1567642425061_img_000">
    <img  data-src="https://img.36krcdn.com/20190904/v2_1567591358070_img_jpg">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641974410_img_000">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641974454_img_000">
    
  2. 使用 JavaScript 监听滚动事件。loadImage是一个函数

    window.addEventListener('scroll', loadImage);
    
  3. 在滚动事件处理函数中,判断图片是否进入视口,若进入则将data-src的值赋给src,实现图片的加载。使用clientHeightscrollTopoffsetTop获取相关的高度,当图片所在位置的高度offsetTop小于 屏幕高度clientHeight和滚动条高度scrollTop之和,就加载该图片

            function loadImage(){
                //一屏的高度
                let screenHeight = document.documentElement.clientHeight 
                // console.log(screenHeight)
                //滚动条距离顶部的高度  不同浏览器的兼容性问题
                let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
                // console.log(scrollTop)
                for(let i=0;i<num;i++){
                    // console.log(imgs[i].offsetTop)
                    if(imgs[i].offsetTop<screenHeight+scrollTop){
                        //数据属性
                        // console.log(imgs[i].dataset.src)
                        imgs[i].src=imgs[i].dataset.src
                    }
                }
            }
    
  4. 当图片全部加载完成之后,并不需要继续调用事件处理函数,停止发送请求事件。可以添加一个变量来判断图片是否全部加载完成。

        const imgs = document.getElementsByTagName("img");
        const num = imgs.length
        let n=0
        function loadImage(){
            ···其他的代码
            for(let i=0;i<num;i++){
               if(imgs[i].offsetTop<screenHeight+scrollTop){     
                   imgs[i].src=imgs[i].dataset.src
                   n=i+1
                   if(n===num){
                      window.removeEventListener('scroll',loadImage)
                   }
               }

            }
       }
  1. 确保首页在第一次打开的时候是有图片的,我们需要在当整个HTML文档被完全加载和解析完毕之后,触发一次事件来展示首屏中的页面

            document.addEventListener("DOMContentLoaded", () => {
                loadImage()
            })
    
  2. 由于滚动事件触发函数的频率太高了,所以我们要用到之前的防抖节流操作,如果有点忘记了,可以去看看 三分钟轻松搞懂Debounce 和 Throttle。那这里我就不手写了,直接借用第三方库中包装好的。

<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    <script>
        function loadImage(){
           ···其它代码
                        window.removeEventListener('scroll',throttleLayLoad)
                    }
                }
            }
        }
        const throttleLayLoad = _.throttle(loadImage,300)

        window.addEventListener('scroll',throttleLayLoad)
        document.addEventListener("DOMContentLoaded", () => {
            loadImage()
        })
    </script>
  1. 全部代码
<img  data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567642423719_img_000">
    <img   data-src="https://img.36krcdn.com/20190905/v2_1567642425030_img_000">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567642425101_img_000">
    <img data-src="https://img.36krcdn.com/20190905/v2_1567642425061_img_000">
    <img  data-src="https://img.36krcdn.com/20190904/v2_1567591358070_img_jpg">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641974410_img_000">
    <img  data-src="https://img.36krcdn.com/20190905/v2_1567641974454_img_000">
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    <script>
        const imgs = document.getElementsByTagName("img");
        const num = imgs.length
        let n=0
        // window.addEventListener('scroll',loadImage)

        function loadImage(){
            console.log('调用了loadImage')

            let screenHeight = document.documentElement.clientHeight //一屏的高度
            // console.log(screenHeight)
            //滚动条距离顶部的高度  不同浏览器的兼容性问题
            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
            // console.log(scrollTop)
            for(let i=0;i<num;i++){
                // console.log(imgs[i].offsetTop)
                if(imgs[i].offsetTop<screenHeight+scrollTop){
                    //数据属性
                    // console.log(imgs[i].dataset.src)
                    imgs[i].src=imgs[i].getAttribute('data-src')
                    n=i+1
                    if(n===num){
                        window.removeEventListener('scroll',throttleLayLoad)
                    }
                }

            }
        }
        const throttleLayLoad = _.throttle(loadImage,500)

        window.addEventListener('scroll',throttleLayLoad)
        document.addEventListener("DOMContentLoaded", () => {
            loadImage()
        })
    </script>
  1. 如果在页面中显示不出来,可以加点css样式

            body{
                background-color: gray;
            }
            img{
                display: block;
                margin-bottom: 50px;
                width: 400px;
                height: 400px;
            }
            *{
                margin: 0;
                padding: 0;
            }
    
  2. 也可以在浏览器的检查中,看到图片加载的过程

637ac895-0360-42a7-bfb1-a96243405c91.gif

结尾

常用于图片数量较多的页面,如图片库、电商网站的商品列表、新闻网站的文章配图等。这些都是需要运用相关的性能优化技术,而图片懒加载是一种有效的性能优化技术,理解并掌握其原理和实现方法对于提升前端性能和用户体验具有重要意义。

希望这篇文章能帮助您更好地理解图片懒加载这一面试知识点!