Skip to content

懒加载 loading

图片懒加载(Lazy Loading)是一种性能优化策略:只有当图片即将进入浏览器视口(Viewport)时,才开始加载图片资源。这可以显著减少首屏加载时间,节省带宽。

HTML5 引入了 loading 属性,为 <img><iframe> 提供原生懒加载支持。在此之前,开发者需要借助 JavaScript(如 IntersectionObserver)来实现相同的功能。

前置知识

阅读本节前,建议先了解:image-map 图像映射

基础概念

懒加载(Lazy Loading)是一种性能优化策略:只有当图片即将进入浏览器视口时,才开始加载图片资源。这可以显著减少首屏加载时间,节省带宽,降低服务器负载。

HTML5 原生 loading 属性让懒加载变得极其简单——只需一个属性值,无需任何 JavaScript。浏览器原生实现比 JS 方案更高效,因为它能在 HTML 解析阶段就做出加载决策。

语法

loading 属性

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>懒加载</title>
</head>
<body>
  <h1>loading 属性</h1>

  <!-- 立即加载(默认行为) -->
  <img src="hero.jpg"
       alt="首屏大图"
       loading="eager"
       width="1200" height="800">

  <hr>

  <!-- 懒加载:图片进入视口附近才开始下载 -->
  <img src="photo-1.jpg"
       alt="文章配图 1"
       loading="lazy"
       width="800" height="600">

  <img src="photo-2.jpg"
       alt="文章配图 2"
       loading="lazy"
       width="800" height="600">

  <!-- 多张长列表图片 -->
  <img src="photo-3.jpg" alt="文章配图 3" loading="lazy" width="800" height="600">
  <img src="photo-4.jpg" alt="文章配图 4" loading="lazy" width="800" height="600">
  <img src="photo-5.jpg" alt="文章配图 5" loading="lazy" width="800" height="600">
</body>
</html>

详细说明

loading="lazy" 的工作原理

html
<!--
  浏览器处理流程:
  1. 解析 HTML,遇到 loading="lazy" 的 img
  2. 不立即请求 src 指向的资源
  3. 当用户滚动到图片接近视口(通常有距离缓冲区)
  4. 浏览器发起请求,加载图片
  5. 图片加载完成后显示

  注意:设置了 width/height 才能正确预留空间
-->

<!-- 首屏图片:使用 eager 或不设置 loading -->
<img src="hero-banner.jpg"
     alt="首屏横幅"
     loading="eager"
     width="1200" height="400">

<!-- 长内容中的图片:使用 lazy -->
<img src="content-image-1.jpg"
     alt="内容图片 1"
     loading="lazy"
     width="800" height="600">

<img src="content-image-2.jpg"
     alt="内容图片 2"
     loading="lazy"
     width="800" height="600">

loading="lazy" 的触发距离

浏览器在图片距离视口还有一定距离时就开始加载(预加载缓冲区):

浏览器触发距离说明
Chrome视口高度 x 1250px(约)高分辨率屏幕距离更远
Firefox视口高度 x (5% + 125px)较近触发
Safari视口高度 x 300px(约)最近触发
Edge与 Chrome 相同Chromium 内核

loading 属性值对比

行为适用场景
lazy进入视口附近时加载非首屏图片
eager立即加载首屏重要图片
不设置等同于 eager默认行为

何时使用 lazy vs eager

html
<!-- eager:首屏可见区域的内容 -->
<header>
  <img src="logo.png" alt="Logo" width="120" height="40" loading="eager">
</header>

<main>
  <!-- eager:首屏大图(用户第一眼看到) -->
  <img src="hero.jpg" alt="首屏大图" width="1200" height="600" loading="eager">

  <!-- lazy:用户需要滚动才能看到的内容 -->
  <img src="article-img-1.jpg" alt="文章图片 1" width="800" height="500" loading="lazy">
  <img src="article-img-2.jpg" alt="文章图片 2" width="800" height="500" loading="lazy">
  <img src="article-img-3.jpg" alt="文章图片 3" width="800" height="500" loading="lazy">

  <!-- lazy:页脚内容 -->
  <img src="partner-logo-1.png" alt="合作伙伴 1" width="100" height="40" loading="lazy">
  <img src="partner-logo-2.png" alt="合作伙伴 2" width="100" height="40" loading="lazy">
</main>

JavaScript IntersectionObserver 方案

对于更精细的控制,可以使用 JavaScript 的 IntersectionObserver

javascript
// JavaScript:使用 IntersectionObserver 实现懒加载
document.addEventListener('DOMContentLoaded', function() {
  const lazyImages = document.querySelectorAll('img[data-src]');

  const observer = new IntersectionObserver(function(entries) {
    entries.forEach(function(entry) {
      if (entry.isIntersecting) {
        const img = entry.target;
        // 将 data-src 赋值给 src
        img.src = img.dataset.src;
        // 图片开始加载后停止观察
        observer.unobserve(img);
      }
    });
  }, {
    // rootMargin:提前 200px 开始加载
    rootMargin: '200px 0px',
    // threshold:元素可见 0% 时触发
    threshold: 0
  });

  lazyImages.forEach(function(img) {
    observer.observe(img);
  });
});
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JS 懒加载</title>
  <style>
    .lazy-img {
      background-color: #f0f0f0;
      min-height: 200px;
    }
  </style>
</head>
<body>
  <!-- 使用 data-src 存储真实地址,src 使用占位图 -->
  <img class="lazy-img"
       src="placeholder-1x1.png"
       data-src="real-photo.jpg"
       alt="通过 JS 懒加载的图片"
       width="800" height="600">

  <script src="lazy-load.js"></script>
</body>
</html>

HTML loading vs JS IntersectionObserver

对比项loading="lazy"IntersectionObserver
实现难度极简(一个属性)需要 JS 代码
浏览器支持Chrome 77+、Firefox 75+、Edge更广泛
控制粒度简单(开/关)精细(距离、阈值)
降级处理忽略属性,立即加载需要 noscript 回退
适用元素<img><iframe>任意 DOM 元素

实战示例

图片列表懒加载

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片列表懒加载</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      max-width: 900px;
      margin: 0 auto;
      padding: 20px;
    }
    .photo-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
      gap: 16px;
    }
    .photo-grid img {
      width: 100%;
      height: 200px;
      object-fit: cover;
      border-radius: 8px;
      background: #f0f0f0;
    }
  </style>
</head>
<body>
  <h1>摄影作品</h1>
  <p>下拉查看更多作品,图片会在接近视口时自动加载。</p>

  <div class="photo-grid">
    <!-- 首屏 1~2 张图片:eager -->
    <img src="photo-01.jpg" alt="作品 1" width="400" height="300" loading="eager">
    <img src="photo-02.jpg" alt="作品 2" width="400" height="300" loading="eager">

    <!-- 非首屏:lazy -->
    <img src="photo-03.jpg" alt="作品 3" width="400" height="300" loading="lazy">
    <img src="photo-04.jpg" alt="作品 4" width="400" height="300" loading="lazy">
    <img src="photo-05.jpg" alt="作品 5" width="400" height="300" loading="lazy">
    <img src="photo-06.jpg" alt="作品 6" width="400" height="300" loading="lazy">
    <img src="photo-07.jpg" alt="作品 7" width="400" height="300" loading="lazy">
    <img src="photo-08.jpg" alt="作品 8" width="400" height="300" loading="lazy">
    <img src="photo-09.jpg" alt="作品 9" width="400" height="300" loading="lazy">
    <img src="photo-10.jpg" alt="作品 10" width="400" height="300" loading="lazy">
  </div>
</body>
</html>

注意事项

  • loading="lazy" 在首屏图片上使用会延迟首屏渲染,适得其反
  • 必须配合 width/height 使用,否则预留空间可能不正确
  • Safari 对 loading="lazy" 的支持较晚(iOS 15.4+ / Safari 15.4+)
  • loading="lazy" 适用于 <img><iframe>,不适用于 <video>poster
  • SSR/预渲染场景中,loading="lazy" 可能在 HTML 生成阶段就被加载
  • 过度使用 loading="lazy"(如每页只有 3 张图)意义不大

最佳实践

  • 首屏可见的图片使用 loading="eager"(或不设置 loading)
  • 首屏以下的图片使用 loading="lazy"
  • 始终设置 width/height 避免布局偏移
  • 结合 decoding="async" 进一步优化
  • 对于不支持 loading="lazy" 的旧浏览器,使用 IntersectionObserver 作为 polyfill
  • Lighthouse 可以检测懒加载使用是否合理

下一节

继续学习:图片格式与选择

参考链接