From d218789c2b4b484a9496c5cdb221c0da7519890f Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Wed, 11 Dec 2024 15:49:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=99=9A=E6=8B=9F=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E9=98=88=E5=80=BC=E9=80=BB=E8=BE=91=E4=BB=A5=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=80=A7=E8=83=BD=EF=BC=8C=E6=94=B9=E8=BF=9B=E5=8F=AF?= =?UTF-8?q?=E8=A7=86=E5=8C=96=E6=95=B0=E6=8D=AE=E8=AE=A1=E7=AE=97=E5=92=8C?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E5=A4=84=E7=90=86=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=9C=A8=E6=95=B0=E6=8D=AE=E9=87=8F=E8=BE=83=E5=B0=8F=E6=97=B6?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=B8=B2=E6=9F=93=E5=85=A8=E9=83=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 2 +- src/components/VirtualList.vue | 40 ++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/App.vue b/src/App.vue index 45e4674..41e0d39 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,7 +3,7 @@ import ProjectList from './components/ProjectList.vue' import type { ListItem, MenuItem } from '@/types' // 生成模拟数据 -const listData: ListItem[] = Array.from({ length: 789 }, (_, i) => ({ +const listData: ListItem[] = Array.from({ length: 50 }, (_, i) => ({ id: i, name: `project-${i + 1}`, path: `/Users/lanyuanxiaoyao/Project/IdeaProjects/project-${i + 1}`, diff --git a/src/components/VirtualList.vue b/src/components/VirtualList.vue index d77f035..81e9ffd 100644 --- a/src/components/VirtualList.vue +++ b/src/components/VirtualList.vue @@ -31,6 +31,8 @@ const ITEM_PADDING = 16 const BUFFER_COUNT = 10 /** 滚动防抖时间(毫秒) */ const SCROLL_DEBOUNCE_TIME = 16 +/** 虚拟滚动阈值,少于此数量则直接渲染全部 */ +const VIRTUALIZATION_THRESHOLD = 500 // 计算列表项实际总高度 const totalItemHeight = computed(() => @@ -53,8 +55,25 @@ const visibleCount = computed(() => Math.floor(containerHeight.value / totalItemHeight.value) ) +/** + * 是否需要虚拟滚动 + * 当数据量小于阈值时直接渲染全部 + */ +const needVirtualization = computed(() => + props.data.length > VIRTUALIZATION_THRESHOLD +) + // 计算当前需要渲染的数据 const visibleData = computed(() => { + // 数据量小于阈值时,直接返回全部数据 + if (!needVirtualization.value) { + return props.data.map((item, index) => ({ + ...item, + index + })) + } + + // 否则使用虚拟滚动 const visibleStart = Math.max(0, startIndex.value - BUFFER_COUNT) const visibleEnd = Math.min( props.data.length, @@ -69,12 +88,16 @@ const visibleData = 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(() => - props.data.length * totalItemHeight.value + needVirtualization.value + ? props.data.length * totalItemHeight.value + : 'auto' ) // 选择处理 @@ -126,9 +149,12 @@ function debounce void>(fn: T, delay: number): (.. /** * 处理滚动事件(已防抖) - * 根据滚动位置更新可视区��的起始索引 + * 根据滚动位置更新可视区的起始索引 */ const handleScroll = debounce(() => { + // 不需要虚拟滚动时直接返回 + if (!needVirtualization.value) return + if (!containerRef.value) return const scrollTop = containerRef.value.scrollTop const newStartIndex = Math.floor(scrollTop / totalItemHeight.value) @@ -253,7 +279,7 @@ function handleKeyDown(e: KeyboardEvent): void { } /** - * 确保选中项在可视区域内 + * 保持选中项在可视区域内 * 如果选中项不可见,则滚动到合适位置 */ function ensureSelectedItemVisible(): void { @@ -302,7 +328,7 @@ onMounted(() => { // 初始化容器高度 updateContainerHeight() - // 添加全局事��监听 + // 添加全局事件监听 window.addEventListener('resize', updateContainerHeight) window.addEventListener('keydown', handleKeyDown) }) @@ -337,12 +363,12 @@ onBeforeUnmount(() => {