From 9eb47f7b097ca605959e6152bf4780810d44e6c7 Mon Sep 17 00:00:00 2001 From: hechang27-sprt Date: Tue, 31 Mar 2026 16:32:44 +0800 Subject: [PATCH] WIP: optimize resize performance --- packages/base/data/list-table-v2.vue | 50 ++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/base/data/list-table-v2.vue b/packages/base/data/list-table-v2.vue index a760f9c..31de4dd 100644 --- a/packages/base/data/list-table-v2.vue +++ b/packages/base/data/list-table-v2.vue @@ -123,6 +123,10 @@ const estimatedHeaderHeight = ref(undefined); let miniTableResizeObserver: ResizeObserver | null = null; let lastMiniTableHeight = 0; +// Track if table is visible in viewport - only probe when visible +const isInViewport = ref(false); +let viewportObserver: IntersectionObserver | null = null; + // Header height constant type TimestampValue = @@ -285,9 +289,10 @@ function estimateTableRowHeight() { // Observe miniTableRef instead of myTableRef - only re-probe if mini-table's rendered height changes // This avoids unnecessary re-probing during window resize when container width changes but row heights stay same onMounted(() => { - // Resize handler - only re-probe if the mini-table's actual height changed + // Resize handler - only re-probe if the mini-table's actual height changed AND table is visible const handleResize = lodash.debounce(async () => { - if (!shouldUseProbeRow.value || miniTableData.value.length === 0) { + // Skip if not using probe, no data, or not visible in viewport + if (!shouldUseProbeRow.value || miniTableData.value.length === 0 || !isInViewport.value) { return; } @@ -315,16 +320,43 @@ onMounted(() => { estimatedRowHeight.value = newRow; estimatedHeaderHeight.value = newHeader; }); - }, 200); + }, 50); - // Observe miniTableRef for height changes - // The mini-table's height only changes when column widths cause text to wrap - // During typical window resize with fixed-height content, height stays same → no re-probe + // ResizeObserver for mini-table height changes miniTableResizeObserver = new ResizeObserver(() => { handleResize(); }); - if (miniTableRef.value) { + // IntersectionObserver to track viewport visibility + // Only attach ResizeObserver when table is visible, detach when off-screen + // This saves CPU for tables scrolled out of view + viewportObserver = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + isInViewport.value = entry.isIntersecting; + + if (entry.isIntersecting) { + // Table became visible - reconnect ResizeObserver + if (miniTableRef.value && miniTableResizeObserver) { + miniTableResizeObserver.observe(miniTableRef.value); + } + } else { + // Table went off-screen - disconnect ResizeObserver to save CPU + // Heights are cached, so no need to keep observing + if (miniTableResizeObserver) { + miniTableResizeObserver.disconnect(); + } + } + }, + { threshold: 0 } // Trigger as soon as any part is visible + ); + + if (myTableRef.value) { + viewportObserver.observe(myTableRef.value); + } + + // Initial observation of mini-table if already in viewport + if (miniTableRef.value && isInViewport.value && miniTableResizeObserver) { miniTableResizeObserver.observe(miniTableRef.value); } }); @@ -335,6 +367,10 @@ onUnmounted(() => { miniTableResizeObserver.disconnect(); miniTableResizeObserver = null; } + if (viewportObserver) { + viewportObserver.disconnect(); + viewportObserver = null; + } }); const containerStyle = computed(() => {