Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Reflow і repaint». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Reflow (layout), це перерахунок геометрії елементів: розмірів, позицій, відступів; repaint (paint), це перемальовування видимих пікселів без перерахунку геометрії.** Reflow спричиняють зміни, що впливають на розкладку: `width`, `height`, `margin`, `padding`, `font-size`, додавання чи видалення вузлів DOM, зміна тексту, resize вікна, а також читання layout-властивостей на кшталт `offsetWidth` одразу після запису стилю (forced synchronous layout). Repaint спричиняють суто візуальні зміни: `color`, `background-color`, `box-shadow`, `outline`. Майже кожен reflow тягне за собою repaint, але не кожен repaint потребує reflow. Найдешевша категорія, composite: `transform` і `opacity` обробляє GPU, не чіпаючи ні layout, ні paint. ```javascript element.style.width = '400px'; // reflow + repaint element.style.backgroundColor = 'red'; // тільки repaint element.style.transform = 'translateX(10px)'; // тільки composite ``` **Ключове:** reflow, найдорожча з трьох операцій, бо layout-дерево пов'язане і зміна одного елемента може змусити перерахувати весь документ; для анімацій використовуйте лише `transform` та `opacity`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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 і opacity** | GPU-анімації не спричиняють 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** | Склейка шарів після paint | `transform`, `opacity` | дешева, виконується на GPU | | **Мета** | Мінімізувати reflow, анімувати лише `transform` і `opacity` | | максимум FPS | ### Типові помилки | Помилка | Що відбувається | | --- | --- | | Багаторазова зміна стилю окремими присвоєннями | Щоразу тригериться новий reflow | | Звернення до `offsetHeight` одразу після зміни стилю | Браузер змушений синхронно перерахувати layout | | Зміна класів у циклі | Reflow на кожній ітерації | | Анімація властивостей, що впливають на layout (`width`, `top`, `margin`) | Layout перераховується на кожному кадрі | | Часте додавання DOM-елементів по одному | Кожна вставка спричиняє перерахунок дерева | Крім того, часто плутають дві речі: думають, що `opacity` завжди безкоштовна. Вона дешева лише тоді, коли елемент уже винесений у власний шар композиції; інакше все одно буде repaint. І ще одна класика, накинути `will-change` на все підряд: кожен шар коштує пам'яті, тож надлишкові шари псують продуктивність замість того, щоб її покращити.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.