Skip to main content

Як оптимізувати великі таблиці?

1. Не рендерити все одразу: пагінація

Найпростіше:

  • показувати тільки частину даних (10-50 рядків на сторінку)
  • решту - через кнопки/пагінацію/"показати ще"
vue
<tr v-for="row in paginatedRows" :key="row.id"> ... </tr>
js
const page = ref(1) const perPage = 50 const paginatedRows = computed(() => { const start = (page.value - 1) * perPage return rows.value.slice(start, start + perPage) })

Ще краще - серверна пагінація: отримувати з бекенду тільки потрібну сторінку.


2. Віртуалізація / windowing (viewport-only rendering)

Для дуже великих таблиць (10k+ рядків) потрібна віртуальна прокрутка:

  • у DOM рендеряться тільки видимі рядки + невеликий буфер зверху/знизу
  • решта - просто порожнє місце з правильною висотою

У Vue зазвичай використовують бібліотеки на кшталт:

  • vue-virtual-scroller
  • vue-virtual-scroll-list

Приклад ідеї (умовний):

vue
<VirtualList :data-key="'id'" :data-sources="rows" :data-component="RowComponent" :kept-alive="50" />

Це радикально знижує кількість DOM-елементів.


3. Мінімізувати реактивність даних

Якщо таблиця просто відображає дані і самі рядки не редагуються:

  • не потрібно робити кожен рядок супер-реактивним
  • можна використовувати:
    • markRaw для великих об'єктів/масивів
    • shallowRef для всього списку
js
import { shallowRef, markRaw } from 'vue' const rows = shallowRef([]) // якщо приходять величезні об'єкти: rows.value = markRaw(bigArray)

Так Vue не буде глибоко відстежувати кожне вкладене поле.


4. Правильний key у v-for

Обов'язково:

vue
<tr v-for="row in rows" :key="row.id">
  • стабільний унікальний key → Vue повторно використовує рядки
  • менше перестворень DOM-елементів
  • краща продуктивність при фільтрації/сортуванні

Не можна використовувати індекс як key (:key="index"), якщо рядки можуть додаватися/видалятися/переставлятися.


5. Важка логіка - у computed, а не в шаблоні

Не робити так:

vue
<td>{{ formatPrice(row.price) }}</td> <td>{{ complexCalculation(row) }}</td>

Краще:

  • заздалегідь прорахувати дані в computed
  • або підготувати поля до передавання в таблицю
js
const preparedRows = computed(() => rows.value.map(row => ({ ...row, priceLabel: formatPrice(row.price), total: calcTotal(row) })) )

6. Не створювати нові об'єкти/функції в шаблоні

Кожен рендер:

vue
<Cell :options="{ align: 'right' }" />
  • створює новий об'єкт → Vue думає, що проп змінився → зайві оновлення.

Винеси такі дані:

js
const rightAlignOptions = { align: 'right' }
vue
<Cell :options="rightAlignOptions" />

7. Оптимізувати фільтрацію та сортування

  • фільтри/пошук → debounce на введення
  • сортування/фільтрація → у computed, а не в watch deep на весь масив
  • при величезних даних - делегувати сортування і фільтрацію серверу
js
const debouncedQuery = ref('') watch(query, debounce((v) => { debouncedQuery.value = v }, 300))

8. Спростити вміст комірок

Всередині <td>:

  • менше складних компонентів
  • не рендерити те, що користувач все одно не бачить (умовні блоки, розкриття за кліком)
  • ліниві деталі: розкриваємо подробиці/детальні картки тільки за запитом

9. Не тягнути все в глобальний стор "про всяк випадок"

Якщо величезна таблиця зберігається в глобальному store:

  • будь-яка зміна стора може тригерити багато компонентів
  • краще зберігати такі дані локально в сторінці/модулі

10. Візуально - скелетони і lazy-loading

З точки зору UX:

  • показувати skeleton замість повної таблиці
  • підвантажувати дані порціями
  • не блокувати UI

Це не зовсім "оптимізація рендеру", але сильно покращує сприйняття.


Як це можна сказати на співбесіді (коротко):

Для оптимізації великих таблиць у Vue я:

  • використовую пагінацію або віртуальний скрол, щоб не рендерити тисячі рядків одразу;
  • мінімізую реактивність (shallowRef/markRaw для великих структур);
  • стежу за key у v-for, щоб Vue повторно використовував рядки;
  • виношу важкі обчислення в computed і не створюю нові об'єкти/функції в шаблоні;
  • за потреби роблю фільтрацію і сортування на сервері, а введення - з debounce.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.