打开浏览器时,它会干一些什么操作呢?--图片懒加载实战带着你一起慢慢体验

1,009 阅读4分钟

当网速慢的时候,你是否有在网上看女神照片时,它是慢慢加载出整张图片的,每当要往下滚动时,它又会慢慢的加载出下一张女神的照片,这时候,作为程序员的你不应该抱怨太慢了,而是要边欣赏女神,边欣赏同僚们的杰作......


一、浏览器HTML文档的解析与渲染

  • 当你打开一个网页时,浏览器会把它的HTML文档下载下来,然后构建DOM树,它反映了文档的结构。
  • 接着它会将css文件下载,然后解析css样式表,构建CSSOM(CSS)对象树,它描述了页面的样式规则。
  • 使用DOM树与CSSOM树构建渲染树,此时会应用样式规则,并且过滤掉不可见的元素。

在下载之前还会进行解析URL、启动DNS查询、建立TCP连接、发送HTTP/HTTPS请求等操作,这里我们先不谈,而是注重于页面渲染和体验懒加载的性能优化。

现在我们打开待会要分享的图片懒加载实例,看看浏览器做的一些事。

浏览器下载.png

image.png

我们可以看到和上述步骤一致,但是,由于JS是单线程的,而浏览器是多线程的,它在解析下载文档和构建渲染树时,如果遇到了img、link、script......它会启动新的下载线程,将所有要下载的内容全部下载到位,这时候,如果下载内容过多,就会影响页面渲染的过程,从而影响用户的体验。

那么,有必要同时并发那么多的图片吗?当然是没有必要的,我们只需要尽快的渲染出页面,也就是先下载用户第一眼能看到的屏幕中的图片,至于下面的图片,完全可以等用户向下滚动时,在进行加载,那么,如何实现呢?

接下来进入今天的实战——图片懒加载


二、图片懒加载

<!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="./common.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
</head>
<body>
    <img src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
    <img src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
    <img src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png">
    <img src="https://img.36krcdn.com/20190905/v2_1567642423719_img_000">
    <img src="https://img.36krcdn.com/20190905/v2_1567642425030_img_000">
    <img src="https://img.36krcdn.com/20190905/v2_1567642425101_img_000">
    <img src="https://img.36krcdn.com/20190905/v2_1567642425061_img_000">
    <img src="https://img.36krcdn.com/20190904/v2_1567591358070_img_jpg">
    <img src="https://img.36krcdn.com/20190905/v2_1567641974410_img_000">
    <img src="https://img.36krcdn.com/20190905/v2_1567641974454_img_000">
</body>
</html>
  • 在body中放了十张图片,如果不做任何处理的话,浏览器会将十张图片全部下载,并且它会尝试并行下载这些资源以加快加载速度,但是,为了避免网络拥塞和服务器过载,游览器对并发的下载数量是有一定限制的。

我们一起来看一下浏览器的下载内容。

image.png

可以看见,它将下面用户未滚到的地方的图片也下载了,页面简单,图片数量少时,这样的作法并不会有太大的区别,但是,企业级开发的网站往往都比较复杂,资源种类也很多,所以,这样高并发的下载是不可行的,

  • 这里我们介绍图片的懒加载技术来解决这个问题。
        const imgs = document.getElementsByTagName('img');
        const num = imgs.length;
        let n = 0
        document.addEventListener("DOMContentLoaded",()=>{
            loadImage()
        })
    • 首先获取DOM元素,并在DOM完全加载完毕后执行loadImage函数。document.addEventListener是一个方法,用于向指定的DOM元素添加事件监听器,DOMContentLoaded是事件名称,表示DOM内容加载完成。
        function loadImage(){
            console.log('你好');
            //是否在可视区?
            let screenHeight = document.documentElement.clientHeight; //一屏的高度
            //滚动条
            //不同浏览器的兼容问题
            let scrollTop = document.documentElement.scrollTop
            || document.body.scrollTop; //滚动条的偏移量
            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){
                        // console.log('所有图片加载完毕');
                        window.removeEventListener('scroll',loadImage)
                    }
                }
            }
        }
    • 定义一个loadImage方法,然后将img标签添加上data-src的属性,用于修改img的src属性,当图片进入可视区时,将data-src的值赋给src, imgs[i].src = imgs[i].dataset.src
    • 判断是否在可视区:获取屏幕高度screenHeight,计算滚动条的偏移量scrollTop,这里考虑兼容性,浏览器能识别并执行document.documentElement.scrollTop的话,就不用考虑document.body.scrollTop,否则,使用后者。
    • 加载图片:遍历每张图片,并记录已经加载的数量n,判断图片offsetTop(相对父元素的顶部边缘距离)是否小于(屏幕高度+滚动条的偏移量),true则为在可视区,设置相应图片的src,进行加载,所以图片加载完成后,移除loadImage方法。

让我们在看一下打开页面的下载

image.png

只下载了前三张要显示的图片。

但是,当我们看动态效果的时候,它似乎每动一点点,还没有到下一张图片时,它也会触发loadImage函数,打印你好。 浏览器加载-Trim.gif

很显然,这里触发的太灵敏了,以至于短短几秒触发了上百次。这里我们直接用引入的js中的_.throttle(loadImage,200),来设置节流,限制它的执行频率。

......
                    if(n === num){
                        // console.log('所有图片加载完毕');
                        window.removeEventListener('scroll',throttleLayLoad)
                    }
                }
            }
        }
        const throttleLayLoad = _.throttle(loadImage,200)
        window.addEventListener('scroll',throttleLayLoad)

节流-Trim.gif

到这,我想咱们就简单的理清了浏览器干了的一些操作,以及图片懒加载的实现,今天的分享就到这啦,最后附上上述代码中简单的common.css文件内容。

*{
    margin: 0;
    padding: 0;
}
body{
    background-color: gray;
}
img{
    display: block;
    margin-bottom:50px ;
    width: 400px;
    height: 400px;
}