前端性能优化:图片懒加载

367 阅读5分钟

浏览器在运行HTML代码的具体操作

1. 处理HTML代码

解析HTML

  • 下载HTML:浏览器向服务器发送请求,下载HTML文档。
  • 解析HTML:浏览器逐行解析HTML文档,将其转换成DOM(文档对象模型)树结构。每个HTML标签都会变成DOM树中的一个节点。

2. 下载和解析CSS

下载CSS

  • CSS下载:在解析HTML时,遇到<link>标签或内联的<style>标签,浏览器会并行下载外部CSS文件。

解析CSS

  • 构建CSSOM:浏览器解析CSS文件,将CSS规则转换成CSSOM(CSS对象模型)树。
  • 应用CSS:将CSS规则应用到对应的DOM节点上,结合DOM树和CSSOM树生成渲染树。

3. 构建渲染树

渲染树

  • 合并树结构:DOM树和CSSOM树合并,形成渲染树。渲染树包含所有需要显示的可见节点。
  • 计算布局:浏览器根据渲染树计算每个元素的几何信息(位置和大小)。

4. 绘制页面

绘制过程

  • 分层处理:复杂页面可能会被分成多个层,以便于独立渲染和优化。
  • 生成绘制列表:浏览器会生成绘制列表,按顺序绘制各个元素。
  • 绘制到屏幕:通过将绘制列表中的内容输出到屏幕上,完成页面渲染。

5. 处理JavaScript

JavaScript执行

  • 遇到JavaScript:当浏览器解析到<script>标签时,会暂停HTML解析,下载并执行JavaScript代码。
  • 单线程:JavaScript在单线程中执行,可能会阻塞HTML解析和页面渲染。
  • 修改DOM和CSSOM:JavaScript可以动态修改DOM和CSSOM树,导致重新计算布局和重新绘制。

6. 浏览器的多线程

多线程架构

  • 资源下载:浏览器使用多个线程并行下载资源,如HTML、CSS、JS、图片等。
  • 渲染进程:通常有一个独立的渲染进程,负责解析和绘制页面。
  • 主线程与工作线程:主线程负责用户交互和脚本执行,工作线程(如Web Worker)可以处理后台任务。

7. 资源下载优化

资源调度

  • 并发限制:浏览器对并发下载的数量有限制,通常每个域名同时下载的资源数量有限。
  • 优先级排序:浏览器会根据资源类型和位置设置下载优先级,优先下载关键资源(如CSS和JS)。

8. 最终页面呈现

综合处理

  • 综合渲染:结合解析的HTML、CSS、JavaScript生成最终页面并呈现给用户。
  • 持续更新:随着用户交互和JavaScript执行,浏览器会不断更新DOM和渲染树,动态修改页面内容。

根据上面描述内容我们可以举个例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <img src="https://n.sinaimg.cn/spider20231205/327/w1700h1027/20231205/36bd-f63d645538dfb5f8d6ef5db2c27d8376.jpg" />
    <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>

我们直接去运行浏览器碰到img src这个标签属性会自动下载这些图片,那么像天猫、淘宝、京东那些网站上有那么多成百上千的图片也会在打开浏览器就下载吗?这会导致用户等待时间过长了,所以就引出我们的主题-图片懒加载。

图片懒加载

什么是图片懒加载呢?图片懒加载(Lazy Loading)是一种优化网页加载速度的技术。它的基本原理是:当你打开一个网页时,页面上很多图片可能在你滚动到它们之前都不会显示出来。懒加载会让这些图片只在你快要看到它们时才下载和显示出来。我们只需要把页面图片给先展示出来,这样就可以避免浏览器过载导致显示页面的时间过长。

那么来看一下他是如何实现的吧:

  1. 首先我们给src前加一个data-前缀使它变成一个数据属性,因为浏览器碰到标签属性会直接加载。而数据属性需通过js来使浏览器加载
<img data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
<img data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
  1. 获取页面中的所有图片元素和图片的总数量,设置一个变量用于追踪已加载的图片数量。
 const imgs=document.getElementsByTagName('img')
 const num=imgs.length
 let n = 0
  1. 设置一个函数用来检查页面上所有图片的位置,判断哪些图片已经进入视口(可见区域),然后将这些图片的实际URL赋值给src属性,从而开始加载图片。
 function loadImage(){
            console.log('hh');    // 可用来检查函数是否有用
            // 是否在可视区
            let screenHeight=document.documentElement.clientHeight  // 一屏的高度
            let scrollTop=document.documentElement.scrollTop || document.body.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].dataset.price);
                    imgs[i].src=imgs[i].getAttribute('data-src')
                    n=i+1
                    if(n===num){
                        window.removeEventListener('scroll' , throttleLayLoad)  // 移除scroll事件写法
                    }
                }
            }
        }
  1. 节流防止滚动事件频繁触发导致性能问题。
const throttleLayLoad=_.throttle(loadImage, 200)
window.addEventListener('scroll', throttleLayLoad);

我们来看一下完整代码:

<!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 data-src="https://n.sinaimg.cn/spider20231205/327/w1700h1027/20231205/36bd-f63d645538dfb5f8d6ef5db2c27d8376.jpg" />
    <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>
        const imgs=document.getElementsByTagName('img')
        const num=imgs.length
        let n = 0
        document.addEventListener('DOMContentLoaded',()=>{
            loadImage()
        })
        function loadImage(){
            console.log('hh');
            // 是否在可视区
            let screenHeight=document.documentElement.clientHeight  // 一屏的高度
            let scrollTop=document.documentElement.scrollTop || document.body.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].dataset.price);
                    imgs[i].src=imgs[i].getAttribute('data-src')
                    n=i+1
                    if(n===num){
                        window.removeEventListener('scroll' , throttleLayLoad)  // 移除scroll事件写法
                    }
                }
            }
        }
        const throttleLayLoad=_.throttle(loadImage, 200)
        window.addEventListener('scroll' , throttleLayLoad)
    </script>
</body>
</html>

上述我们引用了一个样式用来调整图片,然后引用了Lodash库使用其throttle函数用于处理滚动事件。 可以看一下作用效果: ezgif-1-9212d4dbbf.gif

ezgif-7-1702aa8ff4.gif 通过打印hh来判断当图片全加载完是否还会执行loadImage函数,达到检测节流是否成功

总结

图片懒加载作为前端性能优化的重要技术之一,能够显著提升页面的加载速度和用户体验。通过在用户需要时才加载图片,懒加载不仅减少了初次加载的时间和网络带宽的消耗,还使得页面对用户的响应更为迅速和灵敏。实现图片懒加载相对简单,但其效果却是显著的,特别是在内容丰富、图片较多的网页中尤为明显。