前言
有时候列表存在许多图片,那么一次性加载会阻塞 http 请求,为了避免在可视窗口之外的元素进行不必要的图片加载,可以尝试使用懒加载进行优化。懒加载可以显著提高页面加载性能,特别是当页面包含大量图片时。为了实现延迟加载图片(也称为懒加载),可以使用 JavaScript 和 Intersection Observer API。
实现
步骤
- HTML 结构:为每个图片元素设置一个占位符,并使用
data-src
属性存储实际的图片 URL。 - CSS 样式:设置图片占位符的样式。
- JavaScript:使用 Intersection Observer API 监控图片元素,当图片元素进入视口时,加载实际的图片。
代码示例
HTML 结构
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Lazy Load Images</title><style>.placeholder {width: 100%;height: 200px;background-color: #f0f0f0;display: flex;align-items: center;justify-content: center;color: #ccc;}</style>
</head>
<body><h1>Lazy Load Images Example</h1><div class="image-container"><img class="lazy" data-src="image1.jpg" alt="Image 1" class="placeholder"><img class="lazy" data-src="image2.jpg" alt="Image 2" class="placeholder"><img class="lazy" data-src="image3.jpg" alt="Image 3" class="placeholder"><!-- 添加更多图片 --></div><script>document.addEventListener("DOMContentLoaded", function() {const lazyImages = document.querySelectorAll('img.lazy');if ('IntersectionObserver' in window) {const lazyImageObserver = new IntersectionObserver(function(entries, observer) {entries.forEach(function(entry) {if (entry.isIntersecting) {const lazyImage = entry.target;lazyImage.src = lazyImage.dataset.src;lazyImage.classList.remove('lazy');lazyImageObserver.unobserve(lazyImage);}});});lazyImages.forEach(function(lazyImage) {lazyImageObserver.observe(lazyImage);});} else {// Fallback for browsers that do not support IntersectionObserverlet lazyLoadThrottleTimeout;function lazyLoad() {if (lazyLoadThrottleTimeout) {clearTimeout(lazyLoadThrottleTimeout);}lazyLoadThrottleTimeout = setTimeout(function() {const scrollTop = window.pageYOffset;lazyImages.forEach(function(img) {if (img.offsetTop < (window.innerHeight + scrollTop)) {img.src = img.dataset.src;img.classList.remove('lazy');}});if (lazyImages.length == 0) {document.removeEventListener("scroll", lazyLoad);window.removeEventListener("resize", lazyLoad);window.removeEventListener("orientationChange", lazyLoad);}}, 20);}document.addEventListener("scroll", lazyLoad);window.addEventListener("resize", lazyLoad);window.addEventListener("orientationChange", lazyLoad);}});</script>
</body>
</html>
解释
- HTML 结构:
- 使用
data-src
属性存储实际的图片 URL。 - 使用
class="lazy"
标记需要懒加载的图片。
- 使用
- CSS 样式:
- 设置图片占位符的样式,确保在图片加载前显示占位符。
- JavaScript:
- 使用
IntersectionObserver
监控图片元素,当图片元素进入视口时,加载实际的图片。 - 如果浏览器不支持
IntersectionObserver
,使用滚动事件和节流函数实现懒加载。
- 使用
React 代码实例
代码结构
- 创建 React 组件
import React, { useEffect, useRef } from 'react';
import './App.css';const LazyImage = ({ src, alt }) => {const imgRef = useRef();useEffect(() => {const imgElement = imgRef.current;const handleIntersection = (entries, observer) => {entries.forEach(entry => {if (entry.isIntersecting) {const lazyImage = entry.target;lazyImage.src = lazyImage.dataset.src;lazyImage.classList.remove('lazy');observer.unobserve(lazyImage);}});};const observer = new IntersectionObserver(handleIntersection, {root: null, // 使用视口作为根rootMargin: '0px',threshold: 0.1 // 当至少 10% 的图片进入视口时触发});if (imgElement) {observer.observe(imgElement);}return () => {if (imgElement) {observer.unobserve(imgElement);}};}, []);return <img ref={imgRef} data-src={src} alt={alt} className="lazy placeholder" />;
};const App = () => {return (<div className="App"><h1>Lazy Load Images Example</h1><div className="image-container"><LazyImage src="https://via.placeholder.com/300" alt="Image 1" /><LazyImage src="https://via.placeholder.com/300" alt="Image 2" /><LazyImage src="https://via.placeholder.com/300" alt="Image 3" />{/* 添加更多图片 */}</div></div>);
};export default App;
- 添加 CSS 样式
在 App.css 文件中添加以下样式:
.placeholder {width: 100%;height: 200px;background-color: #f0f0f0;display: flex;align-items: center;justify-content: center;color: #ccc;
}
解释
- LazyImage 组件:
- 使用 useRef 获取图片元素的引用。
- 使用 useEffect 在组件挂载时创建 IntersectionObserver 实例,并监控图片元素。
- 当图片元素进入视口时,加载实际的图片,并取消对该图片元素的监控。
- App 组件:
- 渲染多个 LazyImage 组件,每个组件对应一张需要懒加载的图片。
- CSS 样式:
- 设置图片占位符的样式,确保在图片加载前显示占位符。
调试步骤
- 检查图片元素是否被正确观察:
- 在 useEffect 中添加 console.log(imgElement),确保图片元素被正确获取。
- 检查 IntersectionObserver 的回调:
- 在 handleIntersection 函数中添加 console.log(entries),确保回调函数被正确调用。
- 检查图片元素是否进入视口:
- 确保页面布局正确,图片元素确实进入了视口。
兼容性
IntersectionObserver
是现代浏览器支持的 API,如果需要兼容旧版浏览器,可以使用滚动事件和节流函数作为回退方案。
这样,当用户滚动页面时,只有进入视口的图片才会被加载,从而提高页面的加载性能。
总结
但这样不可避免的会存在一定视觉效果上的体验缺失,在页面滚动特别快速时,由于浏览器来不及绘制刚刚进入视图的元素,便会导致出现短暂的白屏现象。这便需要在开发过程中,去做出一定地取舍。