React中如何实现移动端图片列表的按需加载?

UE丶艳艳 阅读 31

在开发移动端图片列表时,我尝试用Intersection Observer实现按需加载,但发现滚动到可视区时图片没及时加载。我按教程写了代码,但控制台报”Uncaught TypeError: observer.observe is not a function”错误,这是为什么?


import { useRef, useEffect } from 'react';

function ImageList({ images }) {
  const observer = useRef();
  
  useEffect(() => {
    observer.current = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // 加载图片逻辑
        }
      });
    });
    
    const targets = document.querySelectorAll('.image-item');
    targets.forEach(target => observer.current.observe(target));
  }, []);

  return (
    
{images.map(img => (
React中如何实现移动端图片列表的按需加载?
))}
); } export default ImageList;

我尝试过把observer.current改为直接创建实例,但滚动到页面底部时新加载的图片不会被观察。是不是需要在组件卸载时销毁观察器?或者Intersection Observer的使用时机哪里错了?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
闲人增芳
你的问题出在 observer.current.observe 这里,因为 observer.current 是一个对象而不是函数。我一般直接把观察器的创建和目标绑定逻辑分开写,这样更清晰。

另外,确实需要在组件卸载时销毁观察器,不然会有内存泄漏的风险。直接用这个改好的代码:

import { useRef, useEffect } from 'react';

function ImageList({ images }) {
const observer = useRef();

useEffect(() => {
observer.current = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 替换为真实图片路径
observer.current.unobserve(img); // 观察完就移除
}
});
});

return () => observer.current.disconnect(); // 卸载时清理
}, []);

return (

{images.map((img, index) => (
key={index}
className="image-item"
data-src={img} // 先放占位符
src=""
alt="lazy"
ref={el => el && observer.current?.observe(el)}
/>
))}

);
}

export default ImageList;


这样写既解决了你的问题,也避免了内存泄漏。懒人开发就是追求简洁有效!
点赞 18
2026-02-01 22:01
Designer°光远
问题出在几个地方,我直接给你一个更好的写法。首先,observer.observe报错是因为你在useEffect里重新赋值了observer.current,而useRef的初始值是undefined,导致第一次渲染时调用的是未定义的方法。其次,确实需要在组件卸载时销毁观察器,不然会造成内存泄漏。

另外,你现在的写法会让所有图片都在一开始就被观察,这并不是按需加载的最佳实践。我们可以通过懒加载的方式,只对最后一个图片进行观察,当它进入视口时再加载下一批图片。

以下是改进后的代码:

import { useRef, useEffect } from 'react';

function ImageList({ images }) {
const observer = useRef();
const lastImageRef = useRef();

useEffect(() => {
if (observer.current) observer.current.disconnect();

observer.current = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 加载更多逻辑
console.log('触发加载更多');
observer.current.disconnect(); // 断开当前观察
}
});
});

if (lastImageRef.current) {
observer.current.observe(lastImageRef.current);
}

return () => {
if (observer.current) observer.current.disconnect();
};
}, [images]);

return (

{images.map((img, index) => (
key={img.id}
className="image-item"
style={{ backgroundImage: url(${img.url}) }}
>
{index === images.length - 1 && }

))}

);
}

export default ImageList;


这里的关键点:
1. 使用lastImageRef来标记最后一个图片,只对它进行观察。
2. 在useEffect中清理之前的观察器实例,避免重复观察。
3. 当最后一个图片进入视口时,断开当前观察并加载更多内容。

这样写更优雅,性能也更好,滚动体验会流畅很多。
点赞 13
2026-01-29 12:00