图片懒加载
一、介绍
定义:图片懒加载是一种延迟加载非视口(viewport)内图片的技术,当用户滚动到图片位置时才加载资源。
核心目的:
- 减少初始页面加载时间
- 节省用户带宽
- 降低服务器压力
- 提升用户体验(特别是移动端)
应用场景:电商网站、图库、长列表、社交媒体等图片密集型页面。
二、实现方式
1. 传统方案(Scroll Event + 位置计算)
<!-- HTML结构 -->
<img class="lazy" data-src="real-image.jpg" src="placeholder.jpg" />
function lazyLoad() {
const lazyImages = document.querySelectorAll('.lazy');
lazyImages.forEach((img) => {
// 获取图片距离视口顶部的距离
const rect = img.getBoundingClientRect();
// 当图片进入视口(+100px预加载区域)
if (rect.top < window.innerHeight + 100) {
img.src = img.dataset.src;
img.classList.remove('lazy');
}
});
}
// 使用节流优化滚动事件
window.addEventListener('scroll', throttle(lazyLoad, 200));
window.addEventListener('load', lazyLoad);
// 节流函数实现
function throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) return;
lastCall = now;
func.apply(this, args);
};
}
2. 现代方案(Intersection Observer API - 推荐)
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img); // 加载后停止观察
}
});
},
{
rootMargin: '100px', // 预加载距离
}
);
document.querySelectorAll('.lazy').forEach((img) => {
observer.observe(img);
});
3. 兼容性处理
if ('IntersectionObserver' in window) {
// 使用现代API
} else {
// 回退到传统方案
window.addEventListener('scroll', throttle(lazyLoad, 200));
}
三、优缺点对比
优点 | 缺点 |
---|---|
▶️ 提升页面加载速度 50%+ | ◼️ 增加代码复杂度 |
▶️ 减少不必要资源请求 | ◼️ 旧浏览器需要兼容处理 |
▶️ 降低服务器带宽消耗 | ◼️ SEO 不友好(需额外处理) |
▶️ 改善移动端用户体验 | ◼️ 快速滚动可能显示空白 |
四、面试高频问题
如何检测图片进入视口?
- 传统方案:
getBoundingClientRect()
+window.innerHeight
- 现代方案:
IntersectionObserver
(性能更优)
- 传统方案:
如何处理滚动性能?
- 使用节流(throttle)或防抖(debounce)
- 现代方案中 API 自带性能优化
占位图方案有哪些?
- 纯色占位
- 低分辨率预览图(LQIP)
- SVG 矢量占位
- CSS 渐变背景
如何兼顾 SEO?
- 在
<noscript>
标签中放置真实图片
<img class="lazy" data-src="image.jpg" src="placeholder.jpg" /> <noscript><img src="image.jpg" /></noscript>
- 在
预加载边界如何设置?
- 通过
rootMargin
参数(现代方案) - 计算时增加偏移量(传统方案)
- 通过
五、最佳实践
优先使用 Intersection Observer
- 原生支持,性能最优
- 支持预加载(
rootMargin
)
响应式图片支持
<img class="lazy" data-srcset="small.jpg 480w, large.jpg 1080w" data-sizes="auto" src="placeholder.jpg" />
加载状态反馈
.lazy { background: #f5f5f5; transition: opacity 0.3s; } .lazy.loaded { opacity: 1; }
错误处理
img.onerror = function () { this.src = 'fallback.jpg'; this.dataset.error = true; };
框架集成方案
- Vue:
vue-lazyload
- React:
react-lazy-load-image-component
- Vue:
六、面试回答策略
概念清晰化:
“懒加载的核心思想是:按需加载。通过延迟非视口资源的加载,优化关键渲染路径”
对比分析:
“传统方案需要手动计算位置,而 Intersection Observer 利用浏览器原生能力,性能更好且代码更简洁”
问题导向:
当被问及性能优化: “在电商项目中,实现懒加载后首屏加载时间减少 40%,Lighthouse 评分提升 30%”
体现深度:
“我们还需要考虑:加载失败时的降级方案、占位图导致的布局偏移(CLS)优化、以及 SSR 场景下的特殊处理”
展示扩展知识:
“除了图片,懒加载也适用于:组件按需加载(React.lazy)、数据分页加载、视频资源等场景”
手写代码提示:
- 先说明实现思路
- 写核心代码段(不必完整)
- 强调关键点(节流/Intersection Observer)
💡 面试金句:
“懒加载不是单纯的技术实现,而是用户体验与性能的平衡艺术。现代浏览器提供的 Intersection Observer API 让我们能以声明式实现高效延迟加载,同时需要为传统浏览器提供平稳退化方案。”