文章目录
- [图片懒加载 前端核心笔记](#图片懒加载 前端核心笔记)
-
- 一、什么是图片懒加载
- 二、实现原理
-
- [1. 核心前提](#1. 核心前提)
- [2. 基础实现步骤](#2. 基础实现步骤)
- 三、具体实现方式
- 四、关键优化点
-
- [1. 占位图设计](#1. 占位图设计)
- [2. 提前加载(预加载)](#2. 提前加载(预加载))
- [3. 加载状态处理](#3. 加载状态处理)
- [4. 性能优化](#4. 性能优化)
- 五、兼容性与场景适配
- 六、常见问题与解决方案
-
- [1. 图片加载后布局跳动(CLS)](#1. 图片加载后布局跳动(CLS))
- [2. 动态添加的图片不触发懒加载](#2. 动态添加的图片不触发懒加载)
- [3. 移动端滚动卡顿](#3. 移动端滚动卡顿)
- 七、总结
图片懒加载 前端核心笔记
一、什么是图片懒加载
图片懒加载(Lazy Load)是一种前端性能优化技术 ,核心思想是:图片不在当前视口(用户可见区域)时,不加载图片资源;当图片进入视口时,再触发加载。
- 核心价值:减少页面初始加载的资源请求数,降低首屏加载时间,节省带宽(尤其移动端/多图场景)。
- 对比传统加载:传统方式下页面所有图片(即使在页面底部)会随页面加载一起请求,懒加载仅加载"看得见"的图片。
二、实现原理
1. 核心前提
- 图片的
src属性是触发资源请求的关键,因此懒加载的核心是先不设置真实的src,仅存储图片地址 ,待条件满足后再赋值给src。 - 监听图片是否进入视口:通过
IntersectionObserver(推荐)或scroll事件+getBoundingClientRect()判断。
2. 基础实现步骤
- 页面渲染时,图片标签使用
data-*(如data-src)存储真实图片地址,src赋值为占位图(可选,如透明图/加载中图标); - 监听滚动/视口变化,判断图片是否进入用户可见区域;
- 满足条件时,将
data-src的值赋值给src,触发图片加载; - 加载完成后,移除监听(避免重复触发)。
三、具体实现方式
方式1:原生 IntersectionObserver(推荐,性能最优)
IntersectionObserver是浏览器原生API,用于监听元素与视口的交叉状态,无需频繁监听scroll事件,性能损耗低。
代码示例
html
<!-- HTML -->
<div class="img-container">
<!-- 占位图 + data-src存储真实地址 -->
<img class="lazy-img" data-src="https://xxx.com/real-img1.jpg" src="placeholder.png" alt="示例图片">
<img class="lazy-img" data-src="https://xxx.com/real-img2.jpg" src="placeholder.png" alt="示例图片">
<img class="lazy-img" data-src="https://xxx.com/real-img3.jpg" src="placeholder.png" alt="示例图片">
</div>
<script>
// 1. 创建观察者实例
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// 2. 判断元素是否进入视口
if (entry.isIntersecting) {
const img = entry.target;
// 3. 赋值真实src,触发加载
img.src = img.dataset.src;
// 4. 加载完成后停止监听该元素
observer.unobserve(img);
// 可选:加载失败降级
img.onerror = () => {
img.src = "error-placeholder.png";
};
}
});
}, {
// 配置项:元素进入视口100px时提前加载(优化体验)
rootMargin: '100px 0px'
});
// 5. 遍历所有懒加载图片,添加监听
document.querySelectorAll('.lazy-img').forEach(img => {
observer.observe(img);
});
</script>
方式2:scroll 事件 + getBoundingClientRect(兼容旧浏览器)
通过监听window.scroll事件,结合getBoundingClientRect()获取元素位置,判断是否进入视口。
⚠️ 注意:需做防抖处理,避免频繁触发scroll导致性能问题。
代码示例
html
<!-- HTML 同方式1 -->
<script>
// 防抖函数:减少scroll事件触发频率
function debounce(fn, delay = 100) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), delay);
};
}
// 懒加载核心逻辑
function lazyLoad() {
const imgs = document.querySelectorAll('.lazy-img');
imgs.forEach(img => {
// 跳过已加载的图片
if (img.src === img.dataset.src) return;
// 获取图片位置:top为元素顶部到视口顶部的距离
const rect = img.getBoundingClientRect();
// 判断是否进入视口(rect.top <= 视口高度)
if (rect.top <= window.innerHeight) {
img.src = img.dataset.src;
img.onerror = () => {
img.src = "error-placeholder.png";
};
}
});
}
// 初始化执行一次(加载首屏可见图片)
lazyLoad();
// 监听scroll事件,防抖执行
window.addEventListener('scroll', debounce(lazyLoad));
// 监听窗口大小变化(适配响应式)
window.addEventListener('resize', debounce(lazyLoad));
</script>
方式3:HTML原生 loading="lazy"(极简,依赖浏览器支持)
现代浏览器(Chrome 77+、Firefox 75+)支持<img>标签的loading属性,无需手写JS,浏览器原生实现懒加载。
代码示例
html
<!-- loading="lazy" 开启原生懒加载 -->
<img src="https://xxx.com/real-img.jpg" loading="lazy" alt="示例图片">
<!-- 可选值:
lazy:懒加载;
eager:立即加载(默认);
auto:由浏览器决定 -->
优缺点
- 优点:零代码、原生支持、性能优;
- 缺点:浏览器兼容性有限(不支持IE/低版本浏览器),无自定义加载阈值(如提前加载)。
四、关键优化点
1. 占位图设计
-
建议使用同比例的纯色占位图/骨架屏,避免图片加载后页面布局跳动(CLS布局偏移);
-
占位图尺寸与真实图片一致,可通过CSS固定宽高:
css.lazy-img { width: 100%; height: 300px; /* 与真实图片比例一致 */ object-fit: cover; }
2. 提前加载(预加载)
- 通过
IntersectionObserver的rootMargin或scroll判断时增加偏移量,让图片在进入视口前100~200px开始加载,提升用户体验。
3. 加载状态处理
- 加载中:显示加载动画/骨架屏;
- 加载失败:设置降级占位图,避免裂图;
- 加载完成:移除占位样式。
4. 性能优化
- 避免在
scroll事件中做复杂计算,必须加防抖; - 图片加载完成后,移除对应的监听(如
observer.unobserve(img)); - 多图场景下,可分批加载(如每次仅处理10张未加载的图片)。
五、兼容性与场景适配
| 实现方式 | 兼容性 | 适用场景 |
|---|---|---|
| IntersectionObserver | IE不支持,现代浏览器支持 | 中高端浏览器、移动端H5 |
| scroll + getBoundingClientRect | 全浏览器支持 | 需兼容IE/低版本浏览器 |
| 原生 loading="lazy" | Chrome77+/Firefox75+ | 无需兼容旧浏览器、极简场景 |
降级方案
js
// 检测IntersectionObserver是否支持,不支持则降级为scroll方式
if (!window.IntersectionObserver) {
// 执行scroll + getBoundingClientRect逻辑
window.addEventListener('scroll', debounce(lazyLoad));
} else {
// 执行IntersectionObserver逻辑
}
六、常见问题与解决方案
1. 图片加载后布局跳动(CLS)
-
原因:图片未设置固定宽高,加载后改变页面布局;
-
解决:给图片容器设置固定宽高,或使用
aspect-ratio属性(CSS3):css.img-container { aspect-ratio: 16/9; /* 图片宽高比 */ overflow: hidden; }
2. 动态添加的图片不触发懒加载
- 原因:初始监听仅执行一次,动态添加的图片未被观察;
- 解决:动态添加图片后,重新调用
observer.observe(newImg)或执行懒加载检测函数。
3. 移动端滚动卡顿
- 原因:
scroll事件触发过频繁,或图片加载占用主线程; - 解决:使用
IntersectionObserver替代scroll,或开启图片will-change: auto优化渲染。
七、总结
- 懒加载核心:延迟加载非视口内图片 ,优先保证首屏性能,核心是控制
src的赋值时机; - 实现方案优先级:原生
loading="lazy">IntersectionObserver>scroll + getBoundingClientRect; - 关键优化:固定图片宽高(避免CLS)、提前加载、处理加载状态、兼容降级;
- 适用场景:长列表图片(如电商商品页、资讯图文页)、移动端H5、多图展示页面。