虚拟滚动是一种非常常见的优化技术,那么本篇文章将介绍如何在 Vue3 中构建虚拟滚动。
原理
虚拟滚动的原理就是一次只渲染固定个的可视区域,每当滚动时,可视区域不会重新渲染,而是复用已经创建完毕的区域,并把滚动导致的数据变化渲染在复用区域。
实现
通过 Composition-api 实现相关功能可以说是非常简单,核心逻辑就是过滤得到已渲染区域对应的数据。已渲染区域一般会比可视区域要更高,这样是为了滚动的时候防止内容出现白条。
下面可以直接阅读核心逻辑是如何实现的。
//defined items array.
//defined itemHeight function
let defaultHeight = 100;
let totalHeight = 0;
let beforeHeight = 0;
const displayItemsRef = shallowRef([]);
const containerRef = ref(null);
const handleChange = () => {
// no container, no handler
const container = containerRef.value;
if (!container) {
return;
}
// 获得滚动条的上部分距离
const scrollTop = container.scrollTop;
const containerHeight = container.clientHeight;
// 需要一点边界空间
const buffer = 2 * defaultHeight;
let beforeHeight = 0;
let contentHeight = 0;
let afterHeight = 0;
for (let i = 0; i < items.length; i++) {
const item = items[i];
const height = itemHeight?.(item) || defaultHeight;
if (beforeHeight < scrollTop - buffer) {
// 顶部
beforeHeight += height;
activeTags[i] = false;
} else if (contentHeight < containerHeight + 3 * buffer) {
// 可渲染部分
contentHeight += height;
activeTags[i] = true;
} else {
// 底部
afterHeight += height;
activeTags[i] = false;
}
}
beforeHeight = beforeHeight;
totalHeight = beforeHeight + afterHeight + contentHeight;
displayItemsRef.value = items.filter((el, index) => activeTags[index]);
};
然后,通过 onMounted 和 onUnmounted,这两个函数触发,并添加相对应的滚动事件。
onMounted(() => {
handleChange();
containerRef.value?.addEventListener?.('scroll', handleChange);
});
onUnmounted(() => {
containerRef.value?.removeEventListener?.('scroll', handleChange);
})
最后,在把对用的 containerRef 添加到相对应的结点上,并把可渲染结点渲染上。
()=>(
<div ref={containerRef} style={{ position: 'relative', overflow: 'auto' }}>
<div style={{ height: totalHeight + 'px' }} />
<div
style={{
position: 'absolute',
width: '100%',
left: 0,
top: beforeHeight + 'px',
}}
>
{dispalyItemsRef.value.map((item, key) => (
<div
key={key}
style={{
backgroundColor: 'black',
color: 'white',
padding: '10px',
height: `${defaultHeight}px`,
}}
>
{el.name}
</div>
)}
</div>
</div>
)
大功告成!
小结
使用 Vue3 提供的 Composition-api 来进行一些组件的编写可以很简单的把一些逻辑抽离,形成可复用的逻辑组件,希望能给你带来新的理解。源码地址:power-ui/components/cdk/scrolling