Виртуализация длинных списков

↑ 7.2 Рендеринг и производительность · ← Предыдущая · → Следующая

Желательно~3 мин чтенияУровень: senior

Зачем это на собесе

Частая задача: таблица на десятки тысяч строк. Показывает понимание стоимости DOM.

Проблема

10 000 строк = сотни тысяч DOM-узлов: долгий рендер, много памяти, тормозящий скролл.

Идея

Рендерим только видимое окно плюс небольшой буфер (overscan). Контейнеру задаём общую высоту, элементы позиционируем transform: translateY.

<script setup lang="ts">
import { useVirtualizer } from '@tanstack/vue-virtual'
 
const parentRef = ref<HTMLElement | null>(null)
const rowVirtualizer = useVirtualizer({
  count: rows.length,
  getScrollElement: () => parentRef.value,
  estimateSize: () => 40,
  overscan: 8,
})
</script>
 
<template>
  <div ref="parentRef" style="height: 600px; overflow: auto">
    <div :style="{ height: rowVirtualizer.getTotalSize() + 'px', position: 'relative' }">
      <div v-for="v in rowVirtualizer.getVirtualItems()" :key="v.key"
           :style="{ position: 'absolute', top: 0, transform: `translateY(${v.start}px)`, height: v.size + 'px' }">
        {{ rows[v.index].name }}
      </div>
    </div>
  </div>
</template>

Готовые решения: @tanstack/vue-virtual, vue-virtual-scroller, q-virtual-scroll (Quasar), QTable с virtual-scroll.

Нюансы

  • переменная высота: измерение (measureElement) или оценка;
  • поиск по странице (Ctrl+F) не найдёт нерендеренные строки;
  • доступность: aria-rowcount, aria-rowindex;
  • альтернатива — пагинация и бесконечная прокрутка; для данных — серверная фильтрация.

Вопросы с ответами