新增虚拟滚动阈值逻辑以优化性能,改进可视化数据计算和滚动处理,确保在数据量较小时直接渲染全部数据。
This commit is contained in:
@@ -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}`,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user