新增虚拟滚动阈值逻辑以优化性能,改进可视化数据计算和滚动处理,确保在数据量较小时直接渲染全部数据。

This commit is contained in:
2024-12-11 15:49:05 +08:00
parent 0a8af1d97b
commit d218789c2b
2 changed files with 34 additions and 8 deletions

View File

@@ -3,7 +3,7 @@ import ProjectList from './components/ProjectList.vue'
import type { ListItem, MenuItem } from '@/types' import type { ListItem, MenuItem } from '@/types'
// 生成模拟数据 // 生成模拟数据
const listData: ListItem[] = Array.from({ length: 789 }, (_, i) => ({ const listData: ListItem[] = Array.from({ length: 50 }, (_, i) => ({
id: i, id: i,
name: `project-${i + 1}`, name: `project-${i + 1}`,
path: `/Users/lanyuanxiaoyao/Project/IdeaProjects/project-${i + 1}`, path: `/Users/lanyuanxiaoyao/Project/IdeaProjects/project-${i + 1}`,

View File

@@ -31,6 +31,8 @@ const ITEM_PADDING = 16
const BUFFER_COUNT = 10 const BUFFER_COUNT = 10
/** 滚动防抖时间(毫秒) */ /** 滚动防抖时间(毫秒) */
const SCROLL_DEBOUNCE_TIME = 16 const SCROLL_DEBOUNCE_TIME = 16
/** 虚拟滚动阈值,少于此数量则直接渲染全部 */
const VIRTUALIZATION_THRESHOLD = 500
// 计算列表项实际总高度 // 计算列表项实际总高度
const totalItemHeight = computed(() => const totalItemHeight = computed(() =>
@@ -53,8 +55,25 @@ const visibleCount = computed(() =>
Math.floor(containerHeight.value / totalItemHeight.value) Math.floor(containerHeight.value / totalItemHeight.value)
) )
/**
* 是否需要虚拟滚动
* 当数据量小于阈值时直接渲染全部
*/
const needVirtualization = computed<boolean>(() =>
props.data.length > VIRTUALIZATION_THRESHOLD
)
// 计算当前需要渲染的数据 // 计算当前需要渲染的数据
const visibleData = computed(() => { const visibleData = computed(() => {
// 数据量小于阈值时,直接返回全部数据
if (!needVirtualization.value) {
return props.data.map((item, index) => ({
...item,
index
}))
}
// 否则使用虚拟滚动
const visibleStart = Math.max(0, startIndex.value - BUFFER_COUNT) const visibleStart = Math.max(0, startIndex.value - BUFFER_COUNT)
const visibleEnd = Math.min( const visibleEnd = Math.min(
props.data.length, props.data.length,
@@ -69,12 +88,16 @@ const visibleData = computed(() => {
// 计算列表偏移量 // 计算列表偏移量
const offsetY = computed(() => const offsetY = computed(() =>
Math.max(0, (startIndex.value - BUFFER_COUNT) * totalItemHeight.value) needVirtualization.value
? Math.max(0, (startIndex.value - BUFFER_COUNT) * totalItemHeight.value)
: 0
) )
// 计算虚拟列表总高度 // 计算虚拟列表总高度
const phantomHeight = computed(() => const phantomHeight = computed(() =>
props.data.length * totalItemHeight.value needVirtualization.value
? props.data.length * totalItemHeight.value
: 'auto'
) )
// 选择处理 // 选择处理
@@ -126,9 +149,12 @@ function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (..
/** /**
* 处理滚动事件(已防抖) * 处理滚动事件(已防抖)
* 根据滚动位置更新可视区<EFBFBD><EFBFBD>的起始索引 * 根据滚动位置更新可视区的起始索引
*/ */
const handleScroll = debounce(() => { const handleScroll = debounce(() => {
// 不需要虚拟滚动时直接返回
if (!needVirtualization.value) return
if (!containerRef.value) return if (!containerRef.value) return
const scrollTop = containerRef.value.scrollTop const scrollTop = containerRef.value.scrollTop
const newStartIndex = Math.floor(scrollTop / totalItemHeight.value) const newStartIndex = Math.floor(scrollTop / totalItemHeight.value)
@@ -253,7 +279,7 @@ function handleKeyDown(e: KeyboardEvent): void {
} }
/** /**
* 保选中项在可视区域内 * 保选中项在可视区域内
* 如果选中项不可见,则滚动到合适位置 * 如果选中项不可见,则滚动到合适位置
*/ */
function ensureSelectedItemVisible(): void { function ensureSelectedItemVisible(): void {
@@ -302,7 +328,7 @@ onMounted(() => {
// 初始化容器高度 // 初始化容器高度
updateContainerHeight() updateContainerHeight()
// 添加全局事<EFBFBD><EFBFBD>监听 // 添加全局事监听
window.addEventListener('resize', updateContainerHeight) window.addEventListener('resize', updateContainerHeight)
window.addEventListener('keydown', handleKeyDown) window.addEventListener('keydown', handleKeyDown)
}) })
@@ -337,12 +363,12 @@ onBeforeUnmount(() => {
<div <div
ref="listRef" ref="listRef"
class="virtual-list-phantom" class="virtual-list-phantom"
:style="{ height: `${phantomHeight}px` }" :style="{ height: needVirtualization ? `${phantomHeight}px` : 'auto' }"
> >
<!-- 实际渲染的列表内容 --> <!-- 实际渲染的列表内容 -->
<div <div
class="virtual-list" class="virtual-list"
:style="{ transform: `translateY(${offsetY}px)` }" :style="needVirtualization ? { transform: `translateY(${offsetY}px)` } : {}"
> >
<!-- 列表项 --> <!-- 列表项 -->
<div <div