大家好,我是“寻找DX3906”。每天进步一点。日积月累,有朝一日定会厚积薄发!
前言:
前面已经和大家分享阿里的了6篇前端面试题:
《【阿里前端面试题】浏览器的加载渲染过程》
《【阿里前端面试题】客户端和服务器交互,为什么选用tcp协议建立链接?》
《【阿里前端面试题】客户端和服务器交互的过程中,丢包是怎么产生的?》
《【阿里前端面试题】知道了解浏览器渲染对自己有什么帮助?》
《【阿里前端面试题】聊聊前端性能优化的方案,解决过什么样的性能问题?》
《【阿里前端面试题】前端页面懒加载的时间分片为什么能对加载性能优化?》
虚拟列表(Virtual List)是一种性能优化技术,用于渲染长列表,通过只渲染可见的列表项来减少DOM操作和提高性能。以下是使用React开发虚拟列表滚动组件的基本步骤和示例代码:
1. 基本组件结构
首先,定义一个虚拟列表组件,它接收列表数据和其他必要的props。
import React, { useState, useRef, useEffect, useCallback } from 'react';
const VirtualList = ({ itemCount, itemHeight, renderItem, overscanCount = 5 }) => {
// ...
return (
<div className="virtual-list" style={{ height: '300px', overflow: 'auto' }}>
<div
className="virtual-list-items"
style={{ transform: `translateY(${startIndex * itemHeight}px)` }}
>
{itemsToRender.map((id) => renderItem(id))}
</div>
</div>
);
};
2. 使用useState和useRef
使用useState
来存储当前滚动位置和渲染的列表项范围,使用useRef
来引用滚动容器和滚动位置。
const [scrollPos, setScrollPos] = useState(0);
const itemsRef = useRef([]);
const listRef = useRef(null);
3. 计算可见项
根据滚动位置和列表项高度,计算当前应该渲染哪些列表项。
useEffect(() => {
const list = listRef.current;
const scrollTop = list.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + visibleItemCount + overscanCount;
const itemsToRender = new Array(endIndex - startIndex);
itemsToRender.forEach((_, index) => {
itemsToRender[index] = renderItem(startIndex + index);
});
setScrollPos(scrollTop);
itemsRef.current = itemsToRender;
}, [itemCount, itemHeight]);
4. 监听滚动事件
使用useCallback
来创建一个处理滚动事件的回调函数,并在组件挂载时添加滚动事件监听器,在卸载时移除。
const handleScroll = useCallback(() => {
if (listRef.current) {
const scrollTop = listRef.current.scrollTop;
setScrollPos(scrollTop);
}
}, [itemHeight]);
useEffect(() => {
listRef.current.addEventListener('scroll', handleScroll);
return () => {
listRef.current.removeEventListener('scroll', handleScroll);
};
}, [handleScroll]);
5. 样式和性能优化
确保虚拟列表和滚动容器有适当的CSS样式,使用transform
属性来移动列表项,以获得更好的性能。
.virtual-list {
position: relative;
width: 100%;
max-height: 300px;
overflow: auto;
}
.virtual-list-items {
position: absolute;
width: 100%;
top: 0;
left: 0;
}
6. 完整的组件示例
将上述代码片段组合成一个完整的虚拟列表组件。
const VirtualList = ({ itemCount, itemHeight, renderItem, overscanCount = 5 }) => {
const [scrollPos, setScrollPos] = useState(0);
const itemsRef = useRef([]);
const listRef = useRef(null);
const visibleItemCount = 20; // 根据容器高度和项高度计算
useEffect(() => {
const list = listRef.current;
const handleScroll = () => {
const scrollTop = list.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight) - overscanCount;
const endIndex = startIndex + visibleItemCount + (2 * overscanCount);
const itemsToRender = [];
for (let i = startIndex; i < endIndex; i++) {
if (i >= 0 && i < itemCount) {
itemsToRender.push(renderItem(i));
}
}
setScrollPos(scrollTop);
itemsRef.current = itemsToRender;
};
list.addEventListener('scroll', handleScroll);
return () => list.removeEventListener('scroll', handleScroll);
}, [itemCount, itemHeight]);
return (
<div className="virtual-list" ref={listRef} style={{ height: '300px', overflow: 'auto' }}>
<div
className="virtual-list-items"
style={{
transform: `translateY(-${Math.floor(scrollPos / itemHeight) * itemHeight}px)`,
position: 'absolute',
top: 0,
}}
>
{itemsRef.current.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
</div>
);
};
这个虚拟列表组件可以根据实际需要进行调整和扩展,例如添加动态高度支持、更复杂的项渲染逻辑等。记住,虚拟列表的关键是在保持高性能的同时,只渲染可见的列表项或近可见区域的项。