原理和介绍

图片懒加载是一种优化网页性能的技术,也叫延迟加载,它延迟加载页面中的图片,直到用户滚动到图片附近时才加载,从而减少初始页面加载时间和带宽使用。

其核心原理是:

  • 将图片地址存储到  data-xxx  属性上,而非src
  • 绑定  scroll  监听事件
  • 判断图片是否在可视区域
  • 如果在,就设置图片  src

只加载当前视口内或即将进入视口的图片,而其他图片则用占位符替代。当用户滚动页面时,检测图片是否进入可视区域,如果是则加载真实图片。

有两种方式实现这种监听,同步(监听scroll事件)、异步(使用Intersection Observer API

同步方案

页面 HTML

<div class="image-container">
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/10/800/600" alt="风景图片1" />
	<div class="caption">图1:美丽的山水风景</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/11/800/600" alt="风景图片2" />
	<div class="caption">图2:宁静的湖泊</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/12/800/600" alt="风景图片3" />
	<div class="caption">图3:壮丽的山脉</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/13/800/600" alt="风景图片4" />
	<div class="caption">图4:海滩日落</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/14/800/600" alt="风景图片5" />
	<div class="caption">图5:绿色山谷</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/15/800/600" alt="风景图片6" />
	<div class="caption">图6:城市天际线</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/16/800/600" alt="风景图片7" />
	<div class="caption">图7:雪山美景</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/17/800/600" alt="风景图片8" />
	<div class="caption">图8:森林小径</div>
  </div>
 
  <div class="lazy-image">
	<div class="placeholder">加载中...</div>
	<img data-src="https://picsum.photos/id/18/800/600" alt="风景图片9" />
	<div class="caption">图9:沙漠景观</div>
  </div>
</div>
 
<div class="loading-info">
  已加载图片: <span id="loaded-count">0</span> /
  <span id="total-count">9</span>
</div>

关键代码

document.addEventListener("DOMContentLoaded", function () {
	// 获取所有需要懒加载的图片
	const lazyImages = document.querySelectorAll("img[data-src]");
	const totalCount = document.getElementById("total-count");
	const loadedCount = document.getElementById("loaded-count");
 
	totalCount.textContent = lazyImages.length;
	loadedCount.textContent = 0;
 
	// 加载图片函数
	function loadImage(img) {
	  const src = img.getAttribute("data-src");
	  if (!src) return;
 
	  img.onload = function () {
		// 图片加载完成后,隐藏占位符
		img.previousElementSibling.style.display = "none";
		// 增加已加载计数
		loadedCount.textContent = parseInt(loadedCount.textContent) + 1;
	  };
 
	  img.src = src;
	  img.removeAttribute("data-src");
	}
 
	// 检查图片是否在视口中
	function checkImages() {
	  lazyImages.forEach((img) => {
		if (img.hasAttribute("data-src")) {
		  const rect = img.getBoundingClientRect();
		  // 当图片顶部在视口底部以上,且图片底部在视口顶部以下时(即图片在视口内)
		  if (rect.top < window.innerHeight && rect.bottom > 0) {
			loadImage(img);
		  }
		}
	  });
	}
 
	// 初始检查
	checkImages();
 
	// 监听滚动事件(使用防抖优化性能)
	let isThrottled = false;
	function throttleCheck() {
	  if (!isThrottled) {
		isThrottled = true;
		setTimeout(() => {
		  checkImages();
		  isThrottled = false;
		}, 100);
	  }
	}
 
	window.addEventListener("scroll", throttleCheck);
	window.addEventListener("resize", throttleCheck);
});

异步方案

使用现代浏览器 API:Intersection Observer API,可以参考这里的[介绍和用例](/01-developer/frontend/intersection_observer_api### 图片懒加载)。