图片通常是网页里最占体积的资源之一。一个页面文字内容可能只有几十 KB,但几张高清图片就能轻松达到几 MB。如果所有图片都在页面打开时一次性加载,首屏速度、移动端流量、服务器带宽都会受到影响。
HTML 图片懒加载的核心思路很简单:用户暂时看不到的图片先不加载,等快要进入可视区域时再加载。这样既能缩短首屏加载时间,也能减少无效请求。本文从原生 loading 属性讲起,再到 IntersectionObserver 自定义懒加载,最后补充性能优化和常见坑。
懒加载适合什么场景
懒加载最适合图片多、页面长、首屏以下资源较重的页面,比如文章详情页、产品列表页、相册页、博客首页、专题页。用户打开页面时,只需要先看到首屏内容,下面的图片可以等滚动到附近再加载。
但并不是所有图片都应该懒加载。首屏核心图片、Logo、头图、影响页面主要视觉结构的图片,通常应该正常加载。如果首屏大图也懒加载,可能会让用户看到明显空白,反而影响体验。
loading 属性
现代浏览器已经支持原生图片懒加载,只需要给 img 标签添加 loading="lazy"。这是最简单、维护成本最低的懒加载方式。
<img src="/images/article-cover.jpg" alt="文章配图">
loading 常见取值有三个:lazy 表示延迟加载,eager 表示立即加载,auto 表示交给浏览器自行判断。实际项目里最常用的是 lazy。

宽高不能省
给图片做懒加载时,一定要尽量写明 width 和 height。如果浏览器不知道图片尺寸,图片加载完成后可能把页面内容往下挤,造成明显的布局跳动。
<img
src="/images/demo.jpg"
alt="示例图片"
width="800"
height="450"
>
布局跳动会影响用户阅读体验,也会影响页面性能指标中的 CLS。对于文章页和产品列表页,图片尺寸固定后,浏览器可以提前预留空间,页面滚动会更稳定。
首屏图片不要懒加载
很多人为了省事,会给页面里所有图片统一加上 loading="lazy",这并不总是正确。首屏大图、文章顶部封面、商品主图等关键图片通常应该使用正常加载,甚至可以配合预加载优化。
<img src="/images/hero.jpg" alt="首屏主图">
如果首屏图片被懒加载,浏览器可能会降低它的加载优先级,导致页面主要内容迟迟不完整。判断标准很简单:用户打开页面马上能看到的图片,不要轻易懒加载;需要向下滚动才能看到的图片,才适合懒加载。
IntersectionObserver 原理
如果需要兼容更复杂的业务,比如加载前显示占位图、进入视口前提前 200 像素加载、图片加载失败后替换兜底图,就可以使用 IntersectionObserver。
它可以观察某个元素是否进入指定区域。相比监听 scroll 事件再反复计算位置,IntersectionObserver 更高效,也更适合大量图片场景。
<img
src="/images/placeholder.jpg"
alt="真实图片"
class="lazy-image"
width="800"
height="450">
上面的写法先把占位图放到 src,真实图片地址放到 data-src。等图片接近可视区域时,再用 JavaScript 把 data-src 的值赋给 src。
自定义懒加载代码
下面是一个基础版实现。为了避免触发部分网站安全策略,示例只展示 JavaScript 逻辑,不把代码包在页面脚本标签里。
const lazyImages = document.querySelectorAll('.lazy-image');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('is-loaded');
observer.unobserve(img);
});
}, {
root: null,
rootMargin: '200px 0px',
threshold: 0.01
});
lazyImages.forEach((img) => observer.observe(img));
rootMargin 可以让图片在进入视口前提前加载。比如 200px 0px 表示图片距离视口还有 200 像素时就开始加载,用户继续下滑时更不容易看到空白。
兼容降级
现在主流浏览器已经支持 IntersectionObserver,但如果你需要兼容旧环境,最好加一个降级逻辑:如果浏览器不支持,就直接加载所有图片,保证内容可见。
if (!('IntersectionObserver' in window)) {
lazyImages.forEach((img) => {
img.src = img.dataset.src;
});
}
降级策略的目标不是做到极致性能,而是保证页面可用。旧浏览器用户即使不能享受懒加载优化,也应该能正常看到图片内容。
配合响应式图片
懒加载解决的是加载时机,不解决图片尺寸本身。如果移动端仍然加载桌面端大图,页面还是会浪费流量。更好的做法是配合 srcset 和 sizes,让浏览器按屏幕宽度选择合适图片。
<img
src="/images/photo-800.jpg"
srcset="/images/photo-400.jpg 400w, /images/photo-800.jpg 800w, /images/photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 800px"
alt="响应式图片示例"
width="800"
height="450">
这样移动端可以加载较小图片,桌面端再加载更大图片。对于图片较多的网站,这类优化往往比单纯加懒加载更明显。
常见错误
第一类错误是懒加载首屏关键图,导致 LCP 变差。第二类错误是不写图片宽高,导致图片加载后页面抖动。第三类错误是只把真实图片放进 data-src,但没有任何降级方案,结果部分环境图片永远不显示。
还有一种常见问题是懒加载距离设置太近。图片已经进入视口才开始请求,用户会看到明显空白。通常可以通过 rootMargin 提前加载,让体验更平滑。
性能优化建议
懒加载只是图片优化的一部分。上线前还要检查图片是否经过压缩,是否使用合适格式,是否有 WebP 或 AVIF 版本,是否配置了缓存策略。对于 WordPress 站点,还要注意主题和插件是否已经自动添加懒加载,避免重复处理。
如果页面图片来自媒体库,建议上传前就控制尺寸,不要把几千像素宽的原图直接塞进正文。文章页常见正文宽度并不需要超大原图,图片越贴近实际展示尺寸,加载速度越稳定。
实践顺序
实际开发中,可以按这个顺序处理图片懒加载:首屏关键图正常加载;正文和列表里的非首屏图片添加 loading="lazy";所有图片补齐 width 和 height;复杂场景再使用 IntersectionObserver;最后用浏览器开发者工具检查网络请求和布局跳动。
如果只是普通文章页,原生 loading="lazy" 已经足够好用;如果是图片瀑布流、商品列表或相册页面,再考虑自定义懒加载逻辑。优化的关键不是代码越复杂越好,而是让首屏更快、滚动更稳、图片请求更合理。














暂无评论内容