在当今互联网时代,用户体验是网站成功的关键因素之一,而网页加载速度直接影响着用户体验。随着网站内容越来越丰富,图像作为信息传递的重要组成部分,其数量和质量都在不断提升,但这也给网页加载带来了巨大挑战。图片懒加载(Lazy Loading)技术应运而生,成为前端性能优化的重要手段之一。本文将探讨图片懒加载的工作原理、实现步骤,并分析它如何帮助提升网页性能。
浏览器是怎么做的?
在了解图片懒加载之前,我们应该先知道在加载图片的时候,浏览器会做些什么事情:
-
请求与接收文档:
- 浏览器先向服务器发送一个 HTTP 请求获取 HTML 文档
- 接收到响应之后,浏览器开始处理接收到的HTML文档
-
解析HTML并构建DOM树:
- 浏览器会先下载 html 标签,然后对 html 标签进行解构并建立DOM树。
-
解析CSS并构建CSSOM树:
- 同时,浏览器会查找页面中的样式信息,包括内联样式、外部CSS文件以及
<style>标签中的样式。它解析这些CSS规则,构建CSSOM(CSS对象模型)树。 - 如果遇到CSS文件,浏览器会异步下载这些文件并构建CSSOM,这个过程与HTML解析可以并行进行。
- 同时,浏览器会查找页面中的样式信息,包括内联样式、外部CSS文件以及
-
构建渲染树:
- 将DOM树和CSSOM树合并,构建渲染树。渲染树只包含渲染页面所需的可见元素及其样式信息,不可见元素(如
display:none)不会被加入此树中。
- 将DOM树和CSSOM树合并,构建渲染树。渲染树只包含渲染页面所需的可见元素及其样式信息,不可见元素(如
-
布局:
- 根据渲染树中的信息,计算每个节点的位置和尺寸。这个过程也叫回流(Reflow)
-
绘制:
- 浏览器根据布局阶段计算出的信息,将渲染树的各个节点绘制到屏幕上。这一步可能涉及调用GPU进行加速渲染。
-
合成与光栅化:
- 在某些浏览器中,页面会被分割成多个图层,这些图层可以独立于主文档进行更新。合成阶段将这些图层组合成最终的屏幕显示。光栅化是将图层转换为像素的过程,可以由专门的光栅化线程完成。
-
JavaScript执行与交互:
- 在页面加载和渲染的过程中,JavaScript代码可以修改DOM结构、CSS样式或触发重新布局和绘制,这可能会中断当前的渲染流程,引起重新计算布局和绘制。
- 现代浏览器尝试通过各种机制(如requestAnimationFrame、async/defer属性)来管理JavaScript执行,以减少对渲染性能的影响。
-
后续交互与渲染循环:
- 用户与页面交互(如滚动、点击)后,可能需要更新渲染树、重新布局和绘制部分或全部页面,这个过程会重复上述的一些步骤。
我们可以看到,浏览器渲染一个页面出来,要经过如此多的步骤,在这个过程中,图片是一个不可忽视的负载项。浏览器通常会为每个<img>标签启动一个新的下载线程,这意味着如果一个页面包含大量图片,将会有大量的并发下载请求。这不仅消耗网络资源,还可能导致页面加载缓慢,尤其是对于移动设备或网络条件不佳的用户来说,体验更是大打折扣。
懒加载
了解完浏览器的渲染过程,我们可以看出大量的图片加载,会让我们的上网体验大打折扣,所以我们就需要通过性能优化来提升我们的上网体验。性能优化的目标是尽快展示首屏内容,让用户感受到即时反馈。对于非首屏的图片,采取懒加载策略可以显著提升这一过程。具体来说,懒加载的核心思想是:仅在图片即将进入可视区域时才开始加载,而非一开始就全部下载。这样既能保证用户看到的内容快速呈现,又能有效减少不必要的网络请求,节省带宽资源。
实现懒加载的步骤
- 使用
data-src替换src属性:默认情况下,不设置src属性(因为设置后浏览器会立即发起请求),而是将图片的真实URL存储在data-src这样的自定义属性中。 - 监听滚动事件:通过JavaScript监听窗口的滚动事件,计算图片位置与当前视口的关系。
- 判断是否进入可视区域:利用
clientHeight(视口高度)、scrollTop(页面已滚动的高度)和offsetTop(元素距离页面顶部的距离)来判断图片是否接近或已经处于可视区域。 - 动态加载图片:一旦图片满足加载条件,立即将
data-src中的URL赋值给src,触发图片下载并显示。 - 优化触发机制:为了减少不必要的计算和事件处理,可以通过防抖(debounce)或节流(throttle)技术来限制滚动事件的触发频率,避免因滚动过快导致的频繁计算和加载请求。
实战
<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-price="20" 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>
// 获取页面上所有的img元素
const imgs = document.getElementsByTagName('img');
// 记录img元素的数量
const num = imgs.length;
// 初始化计数器,追踪已加载的图片数量
let n = 0;
// 当文档内容加载完成时,开始加载图片
document.addEventListener('DOMContentLoaded', () => {
loadImage();
});
// 图片加载函数
function loadImage() {
// 获取一屏高度
let screenHeight = document.documentElement.clientHeight;
// 获取当前滚动条位置,考虑浏览器兼容性
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 遍历所有img元素
for (let i = 0; i < num; i++) {
// 判断图片是否在当前可视区域内
if (imgs[i].offsetTop < screenHeight + scrollTop) {
// 使用数据属性(data-src)来存储原始图片URL,这里打印出来查看
console.log(imgs[i].dataset.src, imgs[i].dataset.price);
// 将data-src属性值赋给src属性,实现图片的真正加载
imgs[i].src = imgs[i].getAttribute('data-src');
// 更新已加载图片数量
n = i + 1;
// 如果所有图片都已加载,则移除滚动事件监听器
if (n === num) {
// 注释掉的代码用于在控制台提示所有图片加载完成
// console.log('所有图片加载完成');
window.removeEventListener('scroll', throttleLayLoad);
}
}
}
}
// 使用lodash库的throttle函数来限制loadImage函数的执行频率,防止滚动过快时频繁执行
const throttleLayLoad = _.throttle(loadImage, 300);
// 添加滚动事件监听器,当用户滚动页面时,触发throttleLayLoad函数
window.addEventListener('scroll', throttleLayLoad);
</script>
</body>
项目展示
我们可以看到,控制台中的src是逐步加载出来的,这大大提升了我们的上网体验
收获与思考
实施图片懒加载,不仅要求前端开发者理解浏览器的工作原理和网络传输机制,还需具备良好的性能优化意识。通过实践懒加载,我们深刻认识到:
- 性能优化是持续的过程:随着技术进步和用户需求变化,不断探索和应用新的优化策略是必要的。
- 理解底层原理的重要性:只有深入理解TCP/IP协议、浏览器渲染机制等底层知识,才能更高效地进行性能调优。
- 细节决定成败:如通过
dataset访问自定义属性、合理使用事件监听优化等,这些细节处理能够显著提升用户体验。 - 用户体验优先:在追求技术实现的同时,始终将提升用户感知的加载速度放在首位。
ok,今天的图片懒加载的分享就到这了,小编还在学习中,欢迎大佬们提出建议!!!非常感谢!!!