背景
1.列表页有一些图片素材,希望点击图片可以预览大图,这里使用的是LightGallery组件 2.在每个单元格中使用LightGallery组件有很严重的性能问题
解决方案
- 列表页只渲染简单的图片信息
- 点击图片时打开lightGallery
代码方案
- index.tsx
import { forwardRef, useCallback, useMemo, useRef, useImperativeHandle } from 'react';
import LightGallery, { LightGalleryProps } from 'lightgallery/react';
import lgThumbnail from 'lightgallery/plugins/thumbnail';
import lgVideo from 'lightgallery/plugins/video';
import lgZoom from 'lightgallery/plugins/zoom';
import lgFull from 'lightgallery/plugins/fullscreen';
import lgAutoplay from 'lightgallery/plugins/autoplay';
import lgRotate from 'lightgallery/plugins/rotate';
import openLg from './open';
type DynamicEl = NonNullable<LightGalleryProps['dynamicEl']>;
export type GalleryItem = DynamicEl[number];
const RenderItem = (item: GalleryItem) => {
const { src, thumb, subHtml, size, title, responsive } = item;
/** 处理缩略图显示 */
const thumbUrl = useMemo(() => {
if (thumb) return thumb;
const isOss = src && (src.includes('oss-cn') || src.includes('oss-cloud'));
return isOss ? `${src}?x-oss-process=image/resize,w_288/quality,q_100` : src;
}, [src, thumb]);
return (
<div
data-lg-size={size}
data-src={src}
data-thumb={thumb}
data-title={title}
data-sub-html={subHtml}
data-responsive={responsive}
data-lg-background-color='rgba(0,0,0,0.85)'
className=' w-24 h-24 rounded-8 overflow-hidden cursor-pointer'
>
<img className=' w-full h-full object-cover' src={thumbUrl} alt={subHtml} />
</div>
);
};
/** 图片预览组件 */
const Lg: any = forwardRef(
({ items, children, onInit, ...restProps }: LightGalleryProps & { items?: DynamicEl }, ref) => {
const lightGalleryRef = useRef<any>(null);
const innerOnInit = useCallback(
(detail: Parameters<NonNullable<LightGalleryProps['onInit']>>[0]) => {
if (detail) {
lightGalleryRef.current = detail.instance;
}
onInit?.(detail);
},
[],
);
const getItems = useCallback(() => {
return items?.map((item, index) => <RenderItem key={item.id || index} {...item} />);
}, [items]);
useImperativeHandle(ref, () => lightGalleryRef.current);
return (
<LightGallery
onInit={innerOnInit}
plugins={[lgZoom, lgThumbnail, lgFull, lgVideo, lgAutoplay, lgRotate]}
infiniteZoom
showZoomInOutIcons
actualSize={false}
closeOnTap
mousewheel
elementClassNames=' flex items-start flex-wrap gap-4'
{...restProps}
>
{children || getItems()}
</LightGallery>
);
},
);
Lg.displayName = 'LightGallery';
Lg.open = openLg;
export default Lg as any as React.ForwardRefExoticComponent<
LightGalleryProps & {
items?: DynamicEl;
} & React.RefAttributes<unknown>
> & {
open: typeof openLg;
};
- open.tsx
import { createRoot, Root } from 'react-dom/client';
import { useRef } from 'react';
import { InitDetail } from 'lightgallery/lg-events';
import LG, { GalleryItem } from './index';
const CONTAINER_ID = 'LIGHT_GALLERY_WRAPPER';
let lightGalleryContainer: Root | null = null;
function LightGalleryWrapper(props: { index: number; items: GalleryItem[] }) {
const lightGalleryRef = useRef<any>(null);
const handleInit = (detail: InitDetail) => {
lightGalleryRef.current = detail.instance;
lightGalleryRef.current?.openGallery(props.index);
};
const handleAfterClose = () => {
lightGalleryRef.current?.destroy();
lightGalleryContainer?.unmount();
document.getElementById(CONTAINER_ID)?.remove();
};
return <LG items={props.items} onInit={handleInit} onAfterClose={handleAfterClose} />;
}
export default function openLg(index: number, items: GalleryItem[]) {
const div = document.createElement('div');
div.id = CONTAINER_ID;
document.body!.append(div);
const root = createRoot(div);
lightGalleryContainer = root;
root.render(<LightGalleryWrapper index={index} items={items} />);
}