在 Taro 中实现懒加载组件,可用于瀑布流布局和组件懒加载
在 Taro 中实现懒加载组件:保留元素高度的解决方案
在开发小程序时,我们常常需要处理长列表的渲染问题。为了优化性能,组件懒加载是一种非常有效的手段。然而,在实现懒加载时,我们需要确保不可见的元素仍然保留它们的高度,以避免位置变化。这篇文章将记录我在 Taro 中实现懒加载组件的过程,并分享我的解决方案。
问题描述
在我的小程序项目中,我需要渲染一个包含大量项目的长列表。为了提高性能,我决定使用懒加载技术,只在需要时加载和渲染可见的项目。然而,我遇到了一个问题:当项目不可见时,它们的高度会消失,导致整个列表的位置发生变化。这种情况会影响用户体验,特别是在快速滚动时。
解决方案
为了解决这个问题,我决定创建一个懒加载组件,该组件在不可见时保留元素的高度。具体步骤如下:
- 监听元素是否进入视口:使用
Taro.createIntersectionObserver
创建一个观察器。 - 动态获取元素高度:使用
Taro.createSelectorQuery
获取元素的高度。 - 保留高度:当元素不可见时,通过
paddingTop
保留高度,避免位置变化。
代码实现
1. 创建 LazyLoadComponent
组件
首先,我们创建一个 LazyLoadComponent
组件,用于包裹需要懒加载的内容。
import React, { useEffect, useRef, useState } from 'react';
import Taro from '@tarojs/taro';
import { View } from '@tarojs/components';
interface LazyLoadComponentProps {
children: React.ReactNode;
threshold: number; // 触发加载的阈值,需要大于100
}
function LazyLoadComponent(props: LazyLoadComponentProps) {
const { children, threshold } = props;
const [isVisible, setIsVisible] = useState(true);
const [elementHeight, setElementHeight] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
const lazyloadContainerId = useRef(`lazyload-${Math.random().toString(36).slice(2)}`);
useEffect(() => {
if (!containerRef.current) return () => {};
getElementBoundingClientRect(`#${lazyloadContainerId.current}`).then((res) => {
setElementHeight(res?.height!);
});
// 创建 IntersectionObserver
const observer = Taro.createIntersectionObserver(this, {
thresholds: [0.1],
observeAll: false,
});
observer.relativeToViewport({ top: threshold }).observe(`#${lazyloadContainerId.current}`, (res) => {
if (res.intersectionRatio > 0.1) {
setIsVisible(true);
} else {
setIsVisible(false);
}
});
return () => {
observer.disconnect();
};
}, [threshold]);
return (
<View
id={lazyloadContainerId.current}
ref={containerRef}
style={{
width: isVisible ? 'auto' : '1px',
height: isVisible ? 'auto' : `${elementHeight}px`,
}}
>
{isVisible ? children : null}
</View>
);
}
export default LazyLoadComponent;
2. 获取元素高度
为了获取每个元素的高度,我们定义了一个辅助函数 getElementBoundingClientRect
。这个函数使用了 Taro.createSelectorQuery
来查询元素的边界矩形,并返回一个 Promise
。
function getElementBoundingClientRect(selector: string) {
return new Promise<BoundingClientRect | undefined>((resolve, reject) => {
function getBounding() {
const $ = Taro.createSelectorQuery();
$.select(selector)
.boundingClientRect()
.exec((res) => {
if (res && res[0]) {
resolve(res[0]);
} else {
resolve(undefined);
}
});
}
try {
const timer = setTimeout(() => {
getBounding();
}, 16);
Taro.nextTick(() => {
clearTimeout(timer);
getBounding();
});
} catch (e) {
reject(e);
}
});
}
3. 使用示例
我们在一个长列表中使用 LazyLoadComponent
来包裹需要懒加载的内容。
import React from 'react';
import LazyLoadComponent from './LazyLoadComponent';
const LongList = () => {
const items = Array.from({ length: 10000 }, (_, index) => index + 1);
return (
<View>
{items.map(item => (
<LazyLoadComponent key={item} threshold={300}>
<View style={{ height: '100px', border: '1px solid black', margin: '10px 0' }}>
Item {item}
</View>
</LazyLoadComponent>
))}
</View>
);
};
export default LongList;
代码解释
- 获取元素高度:使用
Taro.createSelectorQuery
获取元素的高度,并更新elementHeight
状态。 - IntersectionObserver:使用
Taro.createIntersectionObserver
创建一个观察器,监听元素是否进入视口。 - 保留高度:当元素不可见时,通过
paddingTop
保留高度,避免位置变化。
通过上述步骤,我们成功实现了在 Taro 中的懒加载组件,并解决了不可见元素高度消失的问题。希望这篇文章对你有所帮助!