DEV Community

Dennis Zhang
Dennis Zhang

Posted on

图片懒加载组件

1、主要使用IntersectionObserver API

自定义useIntersectionObserver Hook

import { useEffect, useRef, useState } from 'react';

const useIntersectionObserver = (callback, options) => {
  const [entry, setEntry] = useState(null);
  const observer = useRef(null);

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

    observer.current = new IntersectionObserver(([entry]_observer) => {
      setEntry(entry);
      if (entry.isIntersecting) { //  一个布尔值,表示目标元素是否与根元素有交集。
        callback(entry); // 设置src加载图片,将观察对象回传callback
        _observer.unobserve(entry.target);  // 一旦图片加载完成,可以停止观察该元素
      }
    }, options);

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

  return { setTarget: (node) => node && observer.current.observe(node) };
};
Enter fullscreen mode Exit fullscreen mode

创建 LazyImage 组件

import React, { useRef, useState } from 'react';

const LazyImage = ({ src, alt, placeholder }) => {
  const [isLoaded, setIsLoaded] = useState(false);
  const imgRef = useRef();

  const onIntersect = () => {
    if (imgRef.current) {
      imgRef.current.src = src;
      imgRef.current.onload = () => setIsLoaded(true);
    }
  };

  const { setTarget } = useIntersectionObserver(onIntersect, {
    root: null, // 使用视口作为根元素
    rootMargin: '0px',
    threshold: 0.1, // 当至少 10% 的目标元素进入视口时触发回调
  });

  return (
    <div ref={setTarget} style={{ minHeight: '200px', minWidth: '200px' }}>
      <img
        ref={imgRef}
        src={isLoaded ? src : placeholder}
        alt={alt}
        style={{ opacity: isLoaded ? 1 : 0.5, transition: 'opacity 0.5s' }}
      />
    </div>
  );
};

export default LazyImage;
Enter fullscreen mode Exit fullscreen mode

使用 LazyImage 组件

import React from 'react';
import LazyImage from './LazyImage';

const App = () => {
  return (
    <div>
      <h1>Lazy Load Images Example</h1>
      <LazyImage
        src="https://example.com/image.jpg"
        alt="Example Image"
        placeholder="https://via.placeholder.com/200"
      />
      {/* 可以添加更多的 LazyImage 组件 */}
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)