如何实现图片懒加载,原生 + React 实现方式
前言
有时候列表存在许多图片,那么一次性加载会阻塞 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 IntersectionObserver
let 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,如果需要兼容旧版浏览器,可以使用滚动事件和节流函数作为回退方案。
这样,当用户滚动页面时,只有进入视口的图片才会被加载,从而提高页面的加载性能。
总结
但这样不可避免的会存在一定视觉效果上的体验缺失,在页面滚动特别快速时,由于浏览器来不及绘制刚刚进入视图的元素,便会导致出现短暂的白屏现象。这便需要在开发过程中,去做出一定地取舍。