第一次接手公司官网改版项目时,我对着PageSpeed Insights里35分的移动端评分陷入了沉思。瀑布流图显示,整整12MB的未压缩图片在首屏加载时就全量请求,用户需要盯着空白页面等待8秒才能看到完整内容。直到给所有非首屏图片加上懒加载,首屏加载时间直接缩短到1.3秒——这就是为什么说懒加载是前端工程师的必备技能。
现代网页中图片平均占据总资源量的60%以上,电商类站点甚至高达80%。当用户滚动到某个视口区域时才加载对应图片,这种按需加载机制能显著降低:
html复制<img src="placeholder.jpg"
data-src="real-image.jpg"
loading="lazy"
alt="产品展示">
优势:
坑点实录:
<img>标签javascript复制const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
})
}, {
rootMargin: '200px' // 提前200px触发加载
})
document.querySelectorAll('.lazy-img').forEach(img => {
observer.observe(img)
})
性能对比测试:
| 方案 | CPU占用峰值 | 滚动流畅度 | 兼容性 |
|---|---|---|---|
| 原生loading | 12% | 60fps | 85% |
| IntersectionObserver | 18% | 58fps | 92% |
| scroll事件监听 | 43% | 32fps | 100% |
实际项目中发现:当页面存在动态插入内容时,需要手动调用
lozad.observe()更新监听,否则新增图片不会触发加载
css复制.lazy-img {
aspect-ratio: 16/9; /* 提前占位避免布局偏移 */
background: #f5f5f5;
transition: opacity 0.3s;
}
.lazy-img.loaded {
opacity: 1;
}
配合CLS(布局偏移)监控:
javascript复制new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.value > 0.1) {
alert(`CLS超标: ${entry.value}`)
}
}
}).observe({type: 'layout-shift', buffered: true})
html复制<img src="placeholder.jpg"
data-srcset="
small.jpg 480w,
medium.jpg 768w,
large.jpg 1200w"
sizes="(max-width: 600px) 480px,
800px"
class="lazy-img">
CDN优化策略:
SEO灾难:某电商站点因懒加载实现不当,导致Googlebot无法抓取产品图片。解决方案:
html复制<noscript>
<img src="fallback.jpg" alt="...">
</noscript>
内存泄漏:未及时unobserve已加载图片,导致SPA应用内存持续增长。应在组件销毁时执行:
javascript复制observer.disconnect()
瀑布流布局崩溃:使用Masonry等库时,需要在图片加载后手动调用:
javascript复制imagesLoaded(container).on('done', () => {
masonry.layout()
})
骨架屏闪烁:过早移除loading状态会导致用户看到空白。推荐使用:
javascript复制img.onload = () => {
img.classList.add('loaded')
}
弱网环境降级:检测网络类型决定是否启用懒加载:
javascript复制if (navigator.connection.effectiveType === 'slow-2g') {
document.querySelectorAll('[loading=lazy]').forEach(img => {
img.loading = 'eager'
})
}
打印样式遗漏:确保打印时加载所有图片:
css复制@media print {
.lazy-img {
opacity: 1 !important;
}
}
部署后使用Web Vitals监控:
javascript复制import {getCLS, getFID, getLCP} from 'web-vitals'
getCLS(console.log)
getFID(console.log)
getLCP(console.log)
某客户案例优化前后对比:
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| LCP | 4.2s | 1.1s | 73% |
| CLS | 0.45 | 0.02 | 95% |
| 首屏请求数 | 28 | 6 | 78% |
| 跳出率 | 68% | 33% | 51% |
最后分享一个调试技巧:在Chrome DevTools的Network面板,勾选"Disable cache"并限制为Fast 3G,然后滚动页面观察图片加载时机是否合理。我习惯在开发时给未加载图片添加红色边框,已加载的加绿色边框,视觉化检查懒加载范围