在当今的网页开发中,性能优化是至关重要的一环。特别是在电商等图片密集型的网页中,大量图片的加载会严重影响页面的打开速度和用户体验。图片懒加载技术应运而生,它可以显著提升页面的首屏加载速度,减少不必要的网络请求。本文将深入探讨图片懒加载的原理、实现方式以及优化过程。
图片懒加载的基本原理
在传统的网页中,当浏览器解析到 <img>
标签的 src
属性时,会立即发起 HTTP 请求去下载对应的图片。如果页面中有大量图片,同时发起多个请求会导致网络带宽被占满,页面加载速度变慢,甚至可能出现无法打开的情况。
图片懒加载的核心思想是只加载当前可视区域内的图片,当用户滚动页面时,再动态加载即将进入可视区域的图片。这样可以减少首屏加载时的图片数量,提高页面的加载速度。
实现懒加载的关键步骤
- 占位图:在
<img>
标签的src
属性中设置一个小尺寸的占位图,避免一开始就请求原图片地址,减少并发请求和图片大小对性能的影响。占位图通常只需要请求一次,会被浏览器缓存。 - 自定义属性:使用自定义属性
data-original
来存储图片的原地址。这样可以在需要加载图片时,从data-original
中获取原地址并赋值给src
属性。 - 监听滚动事件:通过监听页面的滚动事件,判断图片是否进入可视区域。当图片进入可视区域时,将
data-original
的值赋给src
属性,触发图片的加载。
基础实现:index.html
下面是 index.html
中的代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片懒加载</title>
<style>
* {
margin: 0;
padding: 0;
}
.image-item{
width: 350px;
height: 350px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif" data-original="https://img.36krcdn.com/hsossms/20250313/v2_15ad8ef9eca34830b4a2e081bbc7f57a@000000_oswg172644oswg1536oswg722_img_000?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center" />
<!-- 更多图片... -->
<script>
const viewHeight = document.documentElement.clientHeight;
const eles = document.querySelectorAll('img[data-original][lazyload]');
const lazyload = function () {
Array.prototype.forEach.call(eles,function(item,index){
if(item.dataset.original === "") return;
rect = item.getBoundingClientRect();
if(rect.bottom >= 0 && rect.top <= viewHeight){
(function(){
var img = new Image();
img.src = item.dataset.original;
img.onload = function(){
item.src = item.dataset.original;
item.removeAttribute('data-original');
item.removeAttribute('lazyload');
}
})()
}
})
}
window.addEventListener('scroll', lazyload);
document.addEventListener('DOMContentLoaded', lazyload);
</script>
</body>
</html>
代码解释
- 获取可视区域高度:
const viewHeight = document.documentElement.clientHeight;
获取当前浏览器可视区域的高度。 - 选择需要懒加载的图片:
const eles = document.querySelectorAll('img[data-original][lazyload]');
选择所有带有data-original
和lazyload
属性的<img>
标签。 - 懒加载函数:
lazyload
函数遍历所有需要懒加载的图片,使用getBoundingClientRect()
方法获取图片在视口的位置。如果图片的底部大于等于 0 且顶部小于等于可视区域高度,则表示图片进入了可视区域,此时创建一个新的Image
对象,将data-original
的值赋给src
属性。当图片加载完成后,将src
属性更新为原地址,并移除data-original
和lazyload
属性。 - 监听事件:通过
window.addEventListener('scroll', lazyload);
监听页面的滚动事件,当页面滚动时触发lazyload
函数。同时,通过document.addEventListener('DOMContentLoaded', lazyload);
在页面渲染完成后立即执行一次lazyload
函数,确保首屏的图片能够正常加载。
性能问题
虽然这种实现方式可以实现图片懒加载的基本功能,但存在一些性能问题:
onScroll
触发太频繁:当用户滚动页面时,onScroll
事件会频繁触发,导致lazyload
函数被多次调用,增加了浏览器的负担。forEach
遍历:每次滚动都需要遍历所有需要懒加载的图片,即使有些图片已经加载完成,也会被重复检查,浪费了性能。getBoundingClientRect
触发回流:getBoundingClientRect()
方法会触发浏览器的回流操作,频繁调用会影响页面的性能。
优化实现:observer.html
为了解决上述性能问题,可以使用 IntersectionObserver
API 来实现图片懒加载。IntersectionObserver
是一个异步的 API,它可以在目标元素与视口或指定的根元素交叉时触发回调函数,避免了频繁的滚动事件监听和回流操作。
下面是 observer.html
中的代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.list-item{
height: 400px;
margin: 5px;
list-style: none;
}
.list-item-img{
height: 100%;
}
</style>
</head>
<body>
<ul id="list">
<li class="list-item"><img src="" class="list-item-img" lazyload="true" data-original="https://img.36krcdn.com/hsossms/20250313/v2_15ad8ef9eca34830b4a2e081bbc7f57a@000000_oswg172644oswg1536oswg722_img_000?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center" /></li>
<!-- 更多图片... -->
</ul>
<script>
function addObserver(){
const eles = document.querySelectorAll('img[data-original][lazyload]');
const observer = new IntersectionObserver(function(changes){
changes.forEach(function(element){
if(element.intersectionRatio > 0 && element.intersectionRatio <= 1){
const img = new Image();
img.src = element.target.dataset.original;
img.onload = function(){
element.target.src = element.target.dataset.original;
element.target.removeAttribute('data-original');
element.target.removeAttribute('lazyload');
}
}
})
})
eles.forEach(ele=>{
observer.observe(ele);
})
}
addObserver();
</script>
</body>
</html>
代码解释
- 选择需要懒加载的图片:
const eles = document.querySelectorAll('img[data-original][lazyload]');
选择所有带有data-original
和lazyload
属性的<img>
标签。 - 创建
IntersectionObserver
实例:const observer = new IntersectionObserver(callback);
创建一个IntersectionObserver
实例,并传入一个回调函数。回调函数会在目标元素与视口交叉时被调用,接收一个changes
数组作为参数,数组中的每个元素代表一个目标元素的交叉变化。 - 处理交叉变化:在回调函数中,遍历
changes
数组,通过element.intersectionRatio
判断目标元素与视口的交叉比例。如果交叉比例大于 0 且小于等于 1,则表示目标元素进入了视口,此时创建一个新的Image
对象,将data-original
的值赋给src
属性。当图片加载完成后,将src
属性更新为原地址,并移除data-original
和lazyload
属性。 - 观察目标元素:
eles.forEach(ele=>{ observer.observe(ele); });
遍历所有需要懒加载的图片,使用observer.observe()
方法将它们添加到观察列表中。
优化效果
使用 IntersectionObserver
API 实现图片懒加载的主要优化效果如下:
- 减少
onScroll
次数:IntersectionObserver
是异步的,在浏览器的后台运行,不需要监听onScroll
事件,避免了频繁的滚动事件触发,减少了浏览器的负担。 - 避免回流:
IntersectionObserver
会自动监测目标元素与视口的交叉情况,不需要手动调用getBoundingClientRect()
方法,避免了回流操作,提高了页面的性能。
总结
图片懒加载是一种有效的性能优化技术,可以显著提升页面的首屏加载速度和用户体验。通过使用占位图、自定义属性和监听滚动事件,可以实现基本的图片懒加载功能。但为了进一步优化性能,建议使用 IntersectionObserver
API 来实现,它可以避免频繁的滚动事件监听和回流操作,提高页面的性能和响应速度。