在现代前端开发中,处理大量数据时,性能问题总是不可避免。虚拟列表(Virtual List)是一种有效的解决方案,可以显著提升页面的渲染性能。本文将教你如何使用 JavaScript 手写一个简单的虚拟列表。
什么是虚拟列表?
虚拟列表是一种优化技术,通过只渲染可视区域内的元素,来减少 DOM 节点的数量,从而提升渲染性能。当用户滚动页面时,虚拟列表会动态加载和卸载元素。
实现思路
- 计算可视区域:根据容器的高度和单个项的高度,计算出可视区域内需要展示的项。
- 动态渲染:根据滚动位置动态更新可见项的列表。
- 保持长列表的滚动体验:通过设置容器的总高度,保持滚动条的完整性。
代码实现
下面是一个简单的虚拟列表实现示例:
HTML 结构
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
div {
box-sizing: border-box;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
#container {
height: 400px;
width: 350px;
margin: auto;
border: 1px solid black;
overflow: auto;
}
.item {
display: flex;
height: 41px;
gap: 10px;
}
</style>
</head>
<body>
<div id="container">
<div class="item"></div>
</div>
</body>
</html>
JavaScript 代码
<script>
let data = []
for (let i = 0; i < 200; i++) {
data.push({
id: i,
name: Math.random() * 10 * i
})
}
// 获取页面结构的数据,为了不写死
let containerDom = document.querySelector('#container')
let containerDomHeight = containerDom.clientHeight //不包括边框
let itemDom = document.querySelector('.item')
let itemDomHeight = itemDom.offsetHeight
let maxVisibleMount = Math.round(containerDomHeight / itemDomHeight)
let startIndex = 0
let endIndex = maxVisibleMount - 1
function renderItem() {
let distance = containerDom.scrollTop //滚动距离要先存起来,不能等后面清空
let fragment = document.createDocumentFragment()
let ul = document.createElement('ul')
for (let i = startIndex; i <= endIndex; i++) {
let liDom = document.createElement('li')
liDom.classList.add('item')
liDom.innerHTML = ` <span>${data[i].id}</span> --
<span>${data[i].name}</span>`
ul.appendChild(liDom)
}
fragment.appendChild(ul)
containerDom.innerHTML = ''
containerDom.appendChild(fragment)
ul.style.paddingTop = distance + 'px'
ul.style.paddingBottom = (data.length * itemDomHeight - itemDomHeight * maxVisibleMount - distance) + 'px'
//容器空间占位(特殊注意,box-sizing: border-box下,paddingBottom超过高度设置的值,也会溢出。所以不宜操作container)
}
renderItem() //初始调用一次
containerDom.addEventListener('scroll', function () {
let distance = containerDom.scrollTop
startIndex = Math.ceil(distance / itemDomHeight) - 1 //想象,显示一半的也要显示
endIndex = Math.min(startIndex + maxVisibleMount - 1, data.length - 1)
renderItem()
})
</script>
代码解析
- HTML 结构: 创建一个固定高度的容器用于显示列表。
- 样式设置: 每个项的高度为 设置好。
- JavaScript 实现:
- 通过 计算出可见项的数量。
- 设置容器的总高度,并添加滚动事件监听器。
- 计算当前可见项的索引,并调用
renderItem
渲染这些项。 renderItem
函数负责生成和插入可见项的 HTML。
总结
通过手写虚拟列表,我们可以有效地提升网页的性能,尤其是在处理大量数据时。这个简单的实现提供了一个基本框架,可以根据项目需求进一步扩展功能,比如支持动态加载、虚拟滚动等。