Skip to main content

Reflow і repaint

Reflow, це перерахунок геометрії сторінки (layout), а repaint, це перемальовування пікселів уже порахованих елементів (paint). Коли ви змінюєте DOM або CSS, рушій рендерингу (у Chrome це Blink) мусить перерахувати, як сторінка виглядає, і ця робота ділиться саме на ці етапи.

Теорія

TL;DR

  • Reflow (layout): перерахунок розмірів, позицій і відступів; repaint (paint): перемальовування видимих пікселів без зміни геометрії.
  • Майже кожен reflow спричиняє repaint, але не кожен repaint потребує reflow.
  • Reflow спричиняють width, height, margin, padding, font-size, додавання і видалення вузлів, зміна тексту, resize, scroll.
  • Repaint спричиняють color, background-color, box-shadow, outline, visibility.
  • Читання offsetWidth, clientHeight чи getComputedStyle() одразу після запису стилю змушує браузер зробити синхронний layout (forced reflow).
  • Найдешевший шлях, composite: анімуйте лише transform та opacity, вони йдуть повз layout і paint.

Швидкий приклад

javascript
const el = document.querySelector('.box'); el.style.width = '400px'; // reflow + repaint: змінилася геометрія el.style.backgroundColor = 'red'; // лише repaint: змінився вигляд el.style.transform = 'scale(1.1)'; // лише composite, робота для GPU // Найгірший варіант: запис і одразу читання layout-властивості el.style.width = '500px'; console.log(el.offsetHeight); // forced synchronous layout просто тут

Що робить браузер на кожному етапі

ТермінЩо робитьКоли відбувається
Reflow (layout)Перераховує геометрію елементів: розміри, позиції, відступи, висоту, ширинуКоли змінюється структура DOM або CSS, що впливає на розкладку
Repaint (paint)Перемальовує видимі пікселі (кольори, фон, тіні, рамки) без перерахунку геометріїКоли змінюється лише зовнішній вигляд, а не розмір чи позиція

Під капотом послідовність така.

Reflow змушує браузер:

  1. Перерахувати розміри й позиції елементів (layout tree).
  2. Визначити, які ділянки екрана треба перемалювати.
  3. Запустити repaint.

Repaint зафарбовує змінені пікселі, далі йде composite, склейка шарів.

Майже кожен reflow спричиняє repaint, але не кожен repaint потребує reflow.

Що спричиняє reflow, а що repaint

Reflow спричиняють:

  • зміна розмірів: width, height, padding, margin, border, font-size;
  • додавання або видалення DOM-елементів;
  • зміна тексту;
  • обчислення властивостей, що залежать від layout: offsetWidth, clientHeight, getComputedStyle();
  • зміна класу, який впливає на позиціювання чи розміри;
  • зміна розміру вікна браузера (resize);
  • прокручування (scroll).
javascript
element.style.width = '400px'; // reflow + repaint

Repaint спричиняють:

  • зміна кольору: color, background-color;
  • зміна прозорості opacity;
  • тіні box-shadow;
  • outline;
  • visibility, якщо елемент лишається на місці.
javascript
element.style.backgroundColor = 'red'; // лише repaint

Чому reflow небезпечний для продуктивності

Reflow, це одна з найдорожчих операцій у браузері, особливо коли вона зачіпає багато елементів.

  • Layout-дерево пов'язане: зміна одного елемента може змусити перерахувати весь документ.
  • Один reflow на складній розкладці може займати десятки мілісекунд.

Якщо робити його в циклі або дуже часто, інтерфейс починає гальмувати.

Reflow і repaint відбуваються у фазі рендерингу кадру, між макро- і мікрозадачами event loop. Щоб анімація була плавною, треба вкладатися в 16.6 мс на кадр (60 FPS). Якщо layout займає більше, кадри пропускаються і користувач бачить фризи.

Як зменшити кількість reflow і repaint

ПідхідЩо дає
Batch updatesЗмінюйте кілька властивостей одразу, через classList або cssText, замість окремих присвоєнь
DocumentFragmentЗбирайте DOM-елементи поза документом і вставляйте все одним рухом
Кешуйте обчисленняconst height = el.offsetHeight; один раз перед циклом, а не всередині
Не анімуйте layout-властивостіУникайте анімацій width, height, top, left
CSS transform і opacityGPU-анімації не спричиняють reflow
requestAnimationFrame()Для анімацій замість setInterval
will-changeЗаздалегідь повідомляє браузеру, які властивості зміняться, щоб він виніс елемент у окремий шар

Приклад поганого коду, де reflow відбувається сотні разів:

javascript
for (let i = 0; i < 100; i++) { const el = document.createElement('div'); document.body.appendChild(el); // reflow щоразу el.style.width = i + 'px'; // reflow console.log(el.offsetHeight); // forced reflow }

Оптимізована версія з одним reflow:

javascript
const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { const el = document.createElement('div'); el.style.width = i + 'px'; fragment.appendChild(el); } document.body.appendChild(fragment); // один reflow

Підсумкова таблиця

ТермінЩо робитьПрикладВплив
Reflow (layout)Перерахунок геометрії і позиційзміна width, margin, додавання DOMдорога операція
Repaint (paint)Перемальовування зовнішнього виглядузміна color, background, opacityпомірно витратна
CompositeСклейка шарів після painttransform, opacityдешева, виконується на GPU
МетаМінімізувати reflow, анімувати лише transform і opacityмаксимум FPS

Типові помилки

ПомилкаЩо відбувається
Багаторазова зміна стилю окремими присвоєннямиЩоразу тригериться новий reflow
Звернення до offsetHeight одразу після зміни стилюБраузер змушений синхронно перерахувати layout
Зміна класів у цикліReflow на кожній ітерації
Анімація властивостей, що впливають на layout (width, top, margin)Layout перераховується на кожному кадрі
Часте додавання DOM-елементів по одномуКожна вставка спричиняє перерахунок дерева

Крім того, часто плутають дві речі: думають, що opacity завжди безкоштовна. Вона дешева лише тоді, коли елемент уже винесений у власний шар композиції; інакше все одно буде repaint. І ще одна класика, накинути will-change на все підряд: кожен шар коштує пам'яті, тож надлишкові шари псують продуктивність замість того, щоб її покращити.

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

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

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