很多人一定用过react-loadable,这个库已经很久没有更新了,而且对vite的react项目很不友好,会报错,所以我一般会用react-lazyload来对react项目中的资源做懒加载。
react-loadable: 其实就是对import()加载组件的封装,它是在加载组件的时候,才去动态加载所需的资源。
react-lazyload: 是对IntersectionObserver的封装。
其实懒加载的实现形式只有2种:一种是利用import()动态加载,另一种是利用window的 IntersectionObserver 观察器。import会单独打包,但是观察器不行,import()利用异步加载的方式,在组件渲染的时候,才去异步请求对应的资源。而观察器是资源即将显示的时候,才去加载。
基本使用如下:
说白了就是用监听器先去监听这个元素,当监听到元素以后我们就在他的构造函数里面做想做的事情,就OK了。
1.react-lazyload使用
import img1 from './assets/img1.png';
import img2 from './assets/img2.png';
import LazyLoad from 'react-lazyload';
export default function App() {
return (
<div>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<p>xxxxxx</p>
<LazyLoad placeholder={<div>loading...</div>}>
<img src={img1} />
</LazyLoad>
<LazyLoad placeholder={<div>loading...</div>}>
<img src={img2} />
</LazyLoad>
</div>
);
};
就是用LazyLoad组件包裹需要懒加载的组件,在没有加上之前用 Loading来占位,资源拿到以后就显示资源。
2.手写react-lazyload
搭建基础组件:
import {
FC,
ReactNode,
useState
} from 'react';
interface MyLazyloadProps {
placeholder?: ReactNode,
children: ReactNode,
}
const MyLazyload: FC<MyLazyloadProps> = (props) => {
const {
children,
placeholder,
} = props;
const [visible, setVisible] = useState(false);
return <div >
{visible ? children : placeholder}
</div>;
};
export default MyLazyload;
引入interscetionObserver
import {
FC,
ReactNode,
useState,
useEffect,
useRef
} from 'react';
interface MyLazyloadProps {
placeholder?: ReactNode,
children: ReactNode,
}
const MyLazyload: FC<MyLazyloadProps> = (props) => {
const {
children,
placeholder,
} = props;
const [visible, setVisible] = useState(false);
const observerRef = useRef<IntersectionObserver>();
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const node = containerRef.current as HTMLDivElement;
observerRef.current = new IntersectionObserver((entry) => {
if (entry[0].isIntersecting) {
setVisible(true);
if (node) {
observerRef.current?.unobserve(node);
}
}
});
observerRef.current.observe(node);
() => {
if (node) {
observerRef.current?.unobserve(node);
}
};
});
return <div ref={containerRef}>
{visible ? children : placeholder}
</div>;
};
export default MyLazyload;
就是在useEffect里面用IntersectionOberver监控它自己的div元素,当监控元素出现在页面上的时候,将visible设置成true,显示需要懒加载的元素。
测试看看:
下拉显示图片
你要想做出一个超级棒的封装,就需要给我们的懒加载组件添加上一些属性:
import {
CSSProperties,
FC,
ReactNode,
useRef,
useState
} from 'react';
interface MyLazyloadProps{
className?: string,
style?: CSSProperties,
placeholder?: ReactNode,
offset?: string | number,
width?: number | string,
height?: string | number,
onContentVisible?: () => void,
children: ReactNode,
}
const MyLazyload: FC<MyLazyloadProps> = (props) => {
const {
className = '',
style,
offset = 0,
width,
onContentVisible,
placeholder,
height,
children
} = props;
const containerRef = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
const styles = { height, width, ...style };
return <div ref={containerRef} className={className} style={styles}>
{visible? children : placeholder}
</div>
}
export default MyLazyload;
然后把IntersectionObserver的监听代码加上就好了。