variable-height-virtual-list
v0.1.0
Published
A high-performance virtual list component for variable height items
Maintainers
Readme
Variable Height Virtual List
一个高性能的可变高度虚拟列表组件,支持列表项高度不固定的大数据量渲染场景。
✨ 特性
- 🚀 高性能:只渲染可视区域内的列表项
- 📏 动态高度:支持列表项高度不固定
- 🎯 精准定位:二分查找快速定位起始索引
- 🔄 自动测量:渲染后自动测量并更新真实高度
- 🎨 框架无关:纯 TypeScript 实现,可用于任何框架
- 📦 体积小巧:零依赖,打包体积小
📦 安装
npm install variable-height-virtual-list或使用 pnpm:
pnpm add variable-height-virtual-list🚀 快速开始
基础用法
import { VariableHeightVirtualList } from 'variable-height-virtual-list';
// 获取容器元素
const container = document.querySelector('#list-container');
// 创建虚拟列表实例
const virtualList = new VariableHeightVirtualList({
container: container,
count: 10000, // 总数据量
estimatedItemSize: 100, // 预估高度
overscan: 3, // 缓冲区项数
});
// 设置渲染回调
virtualList.setRenderCallback((index) => {
const item = document.createElement('div');
item.className = 'list-item';
item.textContent = `Item ${index}`;
return item;
});
// 初始化
virtualList.init();HTML 结构
<div id="list-container" style="height: 600px; overflow-y: auto;">
<!-- 虚拟列表会自动在这里创建子元素 -->
</div>CSS 样式(可选)
#list-container {
height: 600px;
overflow-y: auto;
border: 1px solid #ddd;
}
.list-item {
padding: 10px;
border-bottom: 1px solid #eee;
}
.list-item:nth-child(odd) {
background-color: #f9f9f9;
}📚 API 文档
VariableHeightVirtualList
Constructor Options
interface VariableHeightVirtualListOptions {
container: HTMLElement; // 滚动容器元素(必填)
count: number; // 数据总量(必填)
estimatedItemSize?: number; // 预估项高度,默认 100
overscan?: number; // 额外渲染的缓冲项数,默认 3
}方法
setRenderCallback(callback: RenderItemCallback): void
设置列表项渲染回调函数。
virtualList.setRenderCallback((index) => {
const item = document.createElement('div');
item.textContent = `Item ${index}`;
return item;
});init(): void
初始化虚拟列表,开始渲染。
virtualList.init();scrollToIndex(index: number): void
滚动到指定索引位置。
virtualList.scrollToIndex(500); // 滚动到第 500 项getVisibleRange(): { start: number; end: number }
获取当前可见的索引范围。
const { start, end } = virtualList.getVisibleRange();
console.log(`当前显示 ${start} 到 ${end}`);destroy(): void
销毁虚拟列表实例,清理 DOM 和事件监听。
virtualList.destroy();🎯 使用示例
示例 1:简单列表
const virtualList = new VariableHeightVirtualList({
container: document.querySelector('#container'),
count: 10000,
estimatedItemSize: 80,
});
virtualList.setRenderCallback((index) => {
const div = document.createElement('div');
div.className = 'item';
div.textContent = `Item ${index}`;
return div;
});
virtualList.init();示例 2:带图片的复杂列表
const virtualList = new VariableHeightVirtualList({
container: document.querySelector('#container'),
count: 1000,
estimatedItemSize: 150,
overscan: 5,
});
virtualList.setRenderCallback((index) => {
const item = document.createElement('div');
item.className = 'list-item';
const img = document.createElement('img');
img.src = `https://picsum.photos/200/300?random=${index}`;
img.style.width = '80px';
img.style.height = '80px';
const text = document.createElement('p');
text.textContent = `Item ${index}: ${generateRandomText()}`;
item.appendChild(img);
item.appendChild(text);
return item;
});
virtualList.init();
function generateRandomText() {
const length = Math.floor(Math.random() * 100) + 20;
return 'Lorem ipsum dolor sit amet. '.repeat(Math.ceil(length / 25));
}示例 3:与 React 集成
import { useEffect, useRef } from 'react';
import { VariableHeightVirtualList } from 'variable-height-virtual-list';
function VirtualListComponent({ data }) {
const containerRef = useRef(null);
const virtualListRef = useRef(null);
useEffect(() => {
if (!containerRef.current) return;
const virtualList = new VariableHeightVirtualList({
container: containerRef.current,
count: data.length,
estimatedItemSize: 100,
});
virtualList.setRenderCallback((index) => {
const div = document.createElement('div');
div.className = 'item';
div.textContent = data[index].title;
return div;
});
virtualList.init();
virtualListRef.current = virtualList;
return () => {
virtualList.destroy();
};
}, [data]);
return <div ref={containerRef} style={{ height: '600px', overflow: 'auto' }} />;
}🏗️ 工作原理
- 初始预估:根据
estimatedItemSize预估所有项的位置 - 可见区计算:通过二分查找快速定位起始索引
- 渲染可见项:只渲染可视区 + overscan 缓冲区的项
- 高度测量:渲染后测量真实高度并更新位置表
- 位置更新:高度变化时同步更新后续项的位置
⚡ 性能优化
- 使用
requestAnimationFrame节流滚动更新 - 二分查找降低起始索引计算复杂度至 O(log n)
- 只测量和更新可见区域的元素
- 使用
DocumentFragment批量插入 DOM - 通过
measureScheduled避免重复测量
📝 注意事项
- 容器元素必须设置固定高度和
overflow-y: auto estimatedItemSize越接近真实平均高度,性能越好- 图片等异步加载内容可能需要多次测量才能收敛
- 建议为图片设置固定宽高比或占位高度
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 License
MIT © [Your Name]
