实现虚拟列表及优化
- 只渲染当前可视区域
- 可以维护一个对象池,避免频繁创建和销毁 DOM 元素。在滚动时,重新利用之前创建的 DOM 元素,只更新其内容和位置
- 尽量减少重排和重绘操作,可以使用 CSS 属性 transform 替代 top 和 left,避免触发布局的重新计算
- 对滚动事件进行防抖(debounce)和节流(throttle)处理,以减少频繁触发更新列表的次数
- 使用 createDocumentFragment 减少页面的重绘次数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Optimized Virtual List Example with Throttling</title>
<style>
.container {
height: 300px;
overflow-y: scroll;
}
.item {
height: 50px;
border: 1px solid #ccc;
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="container" id="list"></div>
<script>
const totalItems = 1000; // 总项目数
const itemsPerScreen = 10; // 每屏显示的项目数
const itemHeight = 50; // 每个项目的高度
const scrollThrottleTime = 100; // 滚动节流时间间隔
const container = document.getElementById('list');
container.style.height = `${itemsPerScreen * itemHeight}px`;
const itemPool = new Map();
let throttleTimer = null;
function createItem(index) {
const item = document.createElement('div');
item.className = 'item';
item.innerText = `Item ${index}`;
return item;
}
function renderItems(startIndex, endIndex) {
const fragment = document.createDocumentFragment();
for (let i = startIndex; i < endIndex; i++) {
let item = itemPool.get(i);
if (!item) {
item = createItem(i);
itemPool.set(i, item);
}
item.style.transform = `translateY(${i * itemHeight}px)`;
fragment.appendChild(item);
}
container.innerHTML = '';
container.appendChild(fragment);
}
function updateList() {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + itemsPerScreen, totalItems);
renderItems(startIndex, endIndex);
}
function throttleUpdateList() {
if (throttleTimer) {
return;
}
throttleTimer = setTimeout(() => {
updateList();
throttleTimer = null;
}, scrollThrottleTime);
}
updateList();
container.addEventListener('scroll', throttleUpdateList);
</script>
</body>
</html>
总结
想尽一切办法减少 dom 数和操作 dom 的次数