分分钟让你实现图片懒加载,提高用户体验浏览器解析HTML文档并构建DOM树时,会遇到`<img>`标签,这通常触发图片资
图片懒加载(Lazy Loading)是一种常见的前端性能优化技术,用于改善用户体验和页面加载速度。当用户浏览网页时,并非所有图片都需要立即加载到内存中,特别是那些位于屏幕下方、尚未进入可视区域的图片。通过懒加载,我们只在需要的时候加载图片,从而减少初始页面加载时间,节省带宽资源,提升网站性能。
一、浏览器的工作机制与懒加载的必要性
浏览器解析HTML文档并构建DOM树时,会遇到<img>
标签,这通常触发图片资源的立即下载。然而,在现代Web应用中,一般的网页都含多图,如电商网站商品展示,会显著增加服务器负担,拖慢页面渲染速度并消耗额外带宽。以单张1MB图片为例,在10000用户并发访问场景下,瞬时带宽需求可达10GB,对网络资源造成巨大压力。因此,网站需采用优化策略,如图片压缩、懒加载等技术,以提升用户体验和减轻服务器负载。
实现懒加载的基本步骤
- 修改图片源引用: 将
<img>
标签中的src
属性替换为data-src
,后者存储实际的图片URL。浏览器不会解析data-
前缀的属性来下载资源,这样图片就不会在页面加载初期被加载。 - 监听滚动事件: 使用JavaScript监听窗口的
scroll
事件,检查哪些图片进入了可视区域。一旦图片进入可视区域,就将data-src
的值赋给src
,触发图片的实际下载和显示。 - 性能考虑: 为了避免频繁触发
scroll
事件导致的性能问题,可以通过requestAnimationFrame
或节流函数(throttle function)来限制事件处理函数的执行频率,确保即使在快速滚动时也能保持良好的性能。
二、深入理解懒加载的关键点
- 浏览器的多线程机制: 尽管JS是单线程的,但浏览器在处理资源下载时使用了多线程。这意味着图片、CSS和JS文件等资源可以同时下载,提高加载效率。但是,过多的并发下载可能受到网络限制,如TCP/IP连接的数量限制,因此合理控制并发数是必要的。
- DOM操作与性能: 在实现懒加载时,频繁地修改DOM可能会导致重绘和重排,影响性能。因此,应当尽量减少DOM操作,例如在每次滚动后批量更新图片源,而不是逐个处理。
- 数据属性与dataset:
data-
属性是HTML5引入的一个特性,用于存储页面或应用程序的私有自定义数据。通过dataset
属性,可以方便地访问这些数据属性,例如element.dataset.src
获取data-src
的值。
三、实现方式详解
图片懒加载的实现方法多样,下面介绍三种主流的实现方式。
1. 基础懒加载
HTML5引入了loading="lazy"
属性,使得懒加载的实现变得简单。只需在<img>
标签中添加该属性,浏览器便会在图片接近可视区域时自动加载。
<img src="placeholder.jpg" data-src="image-to-lazy-load.jpg" loading="lazy" alt="Lazy Loaded Image">
这里的src
属性放置一个占位符图片,而实际要加载的图片路径存储在data-src
中。这种方案适合于不需要高度定制化的场景。
2. 使用Intersection Observer API
对于需要更精细控制的场景,Intersection Observer API提供了强大的能力。通过监听DOM元素与视口的交集变化,就可以决定何时加载图片。
<!-- HTML 结构 -->
<img class="lazy" data-src="image-to-lazy-load.jpg" alt="Lazy Loaded Image">
<!-- JavaScript 代码 -->
<script>
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = document.querySelectorAll(".lazy");
var observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
observer.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
observer.observe(lazyImage);
});
});
</script>
上面通过创建了一个观察器,当图片与视口发生交集时,会触发图片的真实加载。此方法适用于需要复杂逻辑判断的页面。
3 使用lodash的节流函数来限制loadImage
函数的调用频率来延迟加载图片
<!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">
<!-- 引入lodash库,用于节流函数 -->
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
</head>
<body>
<!-- 图片列表,data-src属性存储真实的图片URL -->
<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">
<!-- 更多图片... -->
<!-- 脚本 -->
<script>
// 获取所有图片元素
const imgs = document.getElementsByTagName('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;
// 遍历所有图片,判断是否需要加载
for (let i = 0; i < num; i++) {
if (imgs[i].offsetTop < screenHeight + scrollTop) {
// 如果图片在可视区域内,则加载图片
imgs[i].src = imgs[i].getAttribute('data-src');
n = i + 1;
if (n === num) {
// 所有图片加载完成后,移除滚动监听
window.removeEventListener('scroll', throttleLayLoad);
}
}
}
}
// 使用lodash的节流函数限制loadImage的调用频率
const throttleLayLoad = _.throttle(loadImage, 300);
// 监听滚动事件,触发节流后的图片加载函数
window.document.addEventListener('scroll', throttleLayLoad);
</script>
</body>
</html>
四、核心价值
图片懒加载的核心价值在于优化页面加载效率,主要体现在以下几个方面:
- 减少初始加载时间:页面不再一次性加载所有图片,而是按需加载,显著缩短了用户等待的时间。
- 节省带宽与服务器资源:通过仅加载可视区域的图片,大幅降低了带宽消耗和服务器的处理负担。
- 提升用户体验:在用户滚动页面时动态加载图片,确保了流畅的浏览体验,尤其是在移动设备上表现更为突出。
五、业务逻辑优化与前端实践的技术
- 预加载与预渲染: 对于即将进入可视区域的图片,可以提前进行预加载,以减少等待时间。预加载通常发生在用户滚动接近图片时,这样可以无缝地展示图片,提升用户体验。
- 使用Intersection Observer API: 这是一个现代的API,提供了更高效的方式来检测元素是否在视口内,替代了传统的滚动事件监听。它允许开发者注册回调函数,当目标元素进入或离开视口时,自动触发回调,无需手动管理事件监听器。
- 减少事件处理器的触发次数: 使用节流(throttle)函数,避免在短时间内频繁执行同一段代码。这有助于减少不必要的计算和DOM操作,提升页面响应速度。
转载自:https://juejin.cn/post/7389650655427788810