Глибока реактивність має ціну: кожен вкладений об'єкт стає проксі, кожне читання поля - виклик track. На списку з десятків тисяч об'єктів з десятками полів це помітно і в пам'яті, і в часі.
Інструменти:
shallowRef- реактивна лише заміна.value, вміст не проксується. Ідеально для даних з API, які не редагують по полю, а замінюють цілком:
const rows = shallowRef([]);
rows.value = await fetchRows(); // рендер спрацює
rows.value[0].name = 'x'; // а тут - ні, і це очікувано
rows.value = [...rows.value]; // явна заміна, щоб оновити
shallowReactive- реактивні лише поля верхнього рівня.markRaw- об'єкт ніколи не стане реактивним: екземпляри Mapbox, Chart.js, редакторів, класи з власним станом. Проксі навколо них не лише дорогий, а й може ламати їхню внутрішню логіку.Object.freezeна даних, які не змінюються, - Vue пропускає їх при перетворенні на реактивні.
Не реактивністю єдиною:
- Віртуалізація списків - рендерити лише видимі рядки (
vue-virtual-scroller, TanStack Virtual). Тисяча DOM-вузлів важча за будь-які проксі. v-memo- пропускати оновлення частини шаблону, доки не змінилися вказані значення.- Стабільні props: передавати в дочірні компоненти примітиви чи стабільні посилання, а не нові об'єкти на кожен рендер.
v-once- для частин шаблону, що відмальовуються раз і не змінюються.
Як знайти проблему: вкладка Performance у Vue DevTools показує час рендеру кожного компонента, а браузерний профайлер - скільки часу йде на проксі й track.
Докладніше в документації: Продуктивність: великі незмінні структури