React中使用懒加载图片时,为什么部分图片在滚动时会重复加载?
我在做一个图片列表页,用Intersection Observer做懒加载。但滚动时发现第5、6张图片会反复触发加载,明明已经离开可视区又回来了。已经试过react-lazyload和自己写hook,都一样:
function ImageList() {
const [loaded, setLoaded] = useState({});
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
setLoaded(prev => ({...prev, [entry.target.id]: true}));
}
});
}, {rootMargin: '0px', threshold: 0.1});
document.querySelectorAll('.lazy-img').forEach(el => observer.observe(el));
return () => observer.disconnect();
}, []);
return (
);
}
控制台没报错,但用性能面板看请求记录,那些图片的src确实被多次发送请求…

不过用index当key不是最佳实践,建议给图片加个唯一id。另外检查一下是不是有些图片的data-src重复了,这也可能导致问题。
IntersectionObserver的行为和你在滚动时的处理方式之间有些小冲突。原理是这样:当图片离开可视区域后,IntersectionObserver并不会自动停止观察它,除非你明确调用observer.unobserve(img)。但你在代码中只在entry.isIntersecting为true时才调用了unobserve,而如果图片滚动回可视区或者有其他情况导致重新进入视口,它又会被重新观察到,从而触发了重复加载。下面是具体的解决方案和优化步骤:
---
### 1. 确保图片只加载一次
你需要记录哪些图片已经加载过,并且确保它们即使再次进入视口也不会重新触发加载逻辑。你已经在状态中存储了加载过的图片信息(
loaded),但没有在IntersectionObserver的回调中检查这个状态。所以需要修改逻辑,在触发加载之前先检查是否已经加载过。---
### 2. 修改代码实现
以下是改进后的代码:
---
### 3. 解释改动的地方
- **加入对
loaded的检查**:在if (entry.isIntersecting)后面增加了一个条件!loaded[img.id],确保只有未加载过的图片才会触发加载逻辑。- **将
loaded加入依赖数组**:这是因为我们在useEffect中使用了loaded,React 需要知道它的变化来重新运行效果函数。- **清理观察器**:通过
observer.disconnect()来确保组件卸载时不会留下无用的观察器。---
### 4. 其他可能的问题点
如果你还是发现有重复加载的情况,可以考虑以下几点:
- **浏览器缓存策略**:有时候浏览器会重新发起请求,但这并不意味着图片被重新下载。可以通过开发者工具中的网络面板查看响应头,确认是否有
Cache-Control或ETag。- **图片 ID 的唯一性**:确保每个图片的
id是唯一的,否则可能会出现状态混乱。---
### 5. 测试建议
改完代码后,记得在不同设备和浏览器下测试一下懒加载的效果。尤其是移动端,滚动行为可能会更复杂。
希望这些改动能帮你解决问题!如果有其他疑问随时问哈~