大家好,我是你们的前端小伙伴FogLetter!今天要和大家聊聊一个既实用又有趣的技术——图片懒加载。这个技术能让你的网页像"懒人"一样聪明,只加载需要的内容,大大提升用户体验!
为什么需要懒加载?
想象一下,你打开一个电商网站,页面有50多张高清大图。如果浏览器一次性加载所有图片,会发生什么?
会出现浏览器疯狂加载所有图片的混乱场景
- 网络带宽被挤爆:就像高峰期的地铁,所有图片同时请求,网络通道拥堵
- 页面响应变慢:用户要等待所有资源加载完才能交互
- 流量浪费:用户可能只看前几张图,却下载了所有图片
懒加载就是解决这些问题的银弹!它让图片只在进入可视区域时才加载,就像一位精明的管家,只在你需要时才提供服务。
懒加载的基本原理
懒加载的核心思想很简单:按需加载。具体实现分几步:
- 占位图策略:先用一个小巧的loading图占位
- 监听滚动事件:当用户滚动页面时检测图片是否进入可视区
- 动态替换:当图片进入可视区时,用真实图片替换占位图
<!-- 示例代码 -->
<img
class="lazy-image"
src="loading.gif"
data-original="real-image.jpg"
lazyload="true"
/>
实现懒加载的详细步骤
1. HTML结构准备
我们给需要懒加载的图片添加特殊标记:
<img
class="image-item"
lazyload="true"
src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
data-original="真实图片地址"
/>
lazyload="true"
:标记需要懒加载的图片src
:使用一个极小的loading图(可缓存,只下载一次)data-original
:存储真实图片地址
2. CSS基础样式
.image-item {
width: 500px;
height: 500px; /* 固定高度防止布局抖动 */
margin-bottom: 20px;
}
注意:给图片容器固定高度很重要,可以避免图片加载时的布局抖动(CLS问题)。
3. JavaScript实现核心逻辑
// 获取可视区域高度
const viewHeight = document.documentElement.clientHeight;
// 获取所有需要懒加载的图片
const lazyImages = document.querySelectorAll('img[data-original][lazyload]');
function lazyLoad() {
lazyImages.forEach(img => {
if(!img.dataset.original) return; // 已加载的跳过
// 获取图片位置信息
const rect = img.getBoundingClientRect();
// 判断是否进入可视区
if(rect.bottom >= 0 && rect.top < viewHeight) {
// 创建一个Image对象预加载
const tempImg = new Image();
tempImg.src = img.dataset.original;
tempImg.onload = () => {
// 真实图片加载完成后替换
img.src = img.dataset.original;
// 移除属性避免重复处理
img.removeAttribute('data-original');
img.removeAttribute('lazyload');
}
}
});
}
// 监听滚动事件
window.addEventListener('scroll', lazyLoad);
// 初始加载
document.addEventListener('DOMContentLoaded', lazyLoad);
4. 关键点解析
-
getBoundingClientRect():获取元素相对于视口的位置
top
:元素顶部到视口顶部的距离bottom
:元素底部到视口顶部的距离
-
可视区域判断:
rect.bottom >= 0 && rect.top < viewHeight
表示图片的底部在视口下方,且顶部还没超出视口
-
Image对象预加载: 先创建一个内存中的Image对象加载图片,等加载完成再替换DOM中的图片,避免半加载状态
性能优化技巧
1. 函数节流
滚动事件触发非常频繁,可以使用节流优化:
function throttle(fn, delay) {
let timer = null; // 1. 定义一个定时器变量
return function() { // 2. 返回一个新函数(闭包)
if(timer) return; // 3. 如果定时器存在,直接退出(节流关键)
timer = setTimeout(() => { // 4. 设置定时器
fn.apply(this, arguments); // 5. 执行原函数,并绑定正确的 `this` 和参数
timer = null; // 6. 执行完成后清空定时器
}, delay);
}
}
window.addEventListener('scroll', throttle(lazyLoad, 200));
2. Intersection Observer API
现代浏览器提供了更高效的API:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.original;
observer.unobserve(img); // 停止观察该元素
}
});
});
lazyImages.forEach(img => observer.observe(img));
这种方法性能更好,但需要考虑兼容性。
3. 预加载相邻图片
可以提前加载可视区附近的图片,提升用户体验:
// 扩展可视区域范围
const preloadHeight = viewHeight * 2;
if(rect.bottom >= -preloadHeight && rect.top < viewHeight + preloadHeight) {
// 加载图片...
}
实际应用中的坑与解决方案
1. 布局抖动问题
现象:图片加载前后高度不一致导致页面跳动
解决:
- 给图片容器设置固定宽高
- 使用CSS aspect-ratio保持宽高比
- 使用padding-top百分比技巧
2. 占位图设计
- 使用纯色占位 + 加载动画
- 使用极小的Base64内联图
- 考虑使用SVG占位图
3. SEO优化
懒加载可能影响搜索引擎抓取图片,解决方案:
<noscript>
<img src="real-image.jpg" alt="图片描述"/>
</noscript>
懒加载的现代解决方案
除了手动实现,还可以使用现成方案:
-
原生loading="lazy":
<img src="image.jpg" loading="lazy" alt="...">
简单但可控性差
-
第三方库:
- lazysizes
- lozad.js
- vue-lazyload(Vue专用)
总结
图片懒加载是提升网页性能的利器,核心要点:
- 按需加载:只加载可视区域内或附近的图片
- 平滑体验:使用占位图避免布局抖动
- 性能优化:节流、Intersection Observer等技巧
- 渐进增强:考虑兼容性和SEO
懒加载就像一位聪明的管家,它知道什么时候该做什么事,既不让主人等待,也不会做无用功。掌握这个技术,让你的网站既快又好!
希望这篇笔记对你有帮助,如果有任何问题,欢迎在评论区留言讨论。下次我会带来更多前端性能优化技巧,敬请期待!
Happy Coding! 🚀