Пояснення про рендеринг у React: оновлення стану для пікселів екрана
Як фази render, reconciliation та commit у React пов’язані з процесом відображення в браузері: layout, paint та composite, від оновлення стану до фактичних пікселів екрана.
Джерела: Rendering Lifecycle in React: From State Update to Pixels on the Screen
Під час роботи з React часто можна почути фразу «компонент перерендерований». Але що насправді означає процес рендерингу?
Тут виникає кілька логічних запитань. Чи негайно React відправляє оновлення до DOM при зміні стану? Чи змушує зміна стану кожен компонент перерендеруватися? І коли React знову рендерує щось, чи означає це, що браузер негайно перемальовує всю сторінку? Виявляється, жодне з цих тверджень не є правдивим.
Щоб по-справжньому зрозуміти життєвий цикл рендерингу, потрібно ознайомитися з двома окремими системами, які працюють разом:
Власний життєвий цикл рендерингу React та процес рендерингу браузера.
Як тільки ви побачите, як ці дві системи пов’язані між собою, такі поняття, як примирення даних, фаза комітування, useEffect, useLayoutEffect, повторне відрендерування та мемоїзація, більше не здаватимуться чимось магічним, а почнуть мати логічне пояснення.
1. Загальна картина
На високому рівні потік дій виглядає так:
User Interaction
│
▼
State / Props Change
│
▼
┌──────────────────────┐
│ React Render │
│ │
│ Component executes │
│ New UI is returned │
└──────────┬───────────┘
│
▼
Reconciliation
│
▼
┌──────────────────────┐
│ Commit Phase │
│ │
│ Update real DOM │
└──────────┬───────────┘
│
▼
┌──────────────────────────┐
│ Browser Rendering │
│ │
│ Style → Layout → Paint │
│ → Composite │
└────────────┬─────────────┘
│
▼
Screen
Основний висновок полягає у наступному:
React визначає, як має виглядати інтерфейс користувача та які зміни потрібні в DOM. Потім браузер перетворює ці зміни DOM на фактичні пікселі на екрані.
2. Що означає „відрендерування“?
Термін „відрендерування“ використовується у кількох значеннях.
У контексті React відрендерування зазвичай означає наступне:
React викликає функцію вашого компонента та визначає, яким має бути результат — інтерфейс користувача.
Розгляньмо цей компонент:
function Counter() {
const [count, setCount] = useState(0);
return (
<button>
Count: {count}
</button>
);
}
Коли React його відображає, виконується тіло функції:
Counter()
↓
returns React element
↓
<button>Count: 0</button>
Це не означає, що браузер вже намалював кнопку на екрані.
Перш ніж відбудеться щось візуальне, React все ще має вирішити, чи справді потрібно змінювати DOM.
Коротко кажучи:
React Render
≠
DOM Update
≠
Browser Paint
Відображення та оновлення DOM — це дві різні операції.
3. Життєвий цикл відображення браузером
Перш ніж заглиблюватися в внутрішню структуру React, корисно зрозуміти, що робить сам браузер.
<h1 class="title">
Hello World
</h1>
У поєднанні з цим стилевим файлом:
.title {
color: red;
font-size: 30px;
}
Перш ніж цей вміст буде візуально видимий, браузер пройде низку внутрішніх кроків.
Спрощена версія цього процесу виглядає так:
HTML
↓
DOM
↓
CSS
↓
CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Composite
↓
Screen
Розгляньмо кожен етап.
4. HTML → DOM
Браузер читає HTML та створює DOM, що скорочено означає Document Object Model.
З урахуванням цього маркування:
<body>
<h1>Hello World</h1>
<p>Welcome</p>
</body>
Браузер створює деревоподібну структуру приблизно такою:
Document
│
└── body
├── h1
│ └── "Hello World"
│
└── p
└── "Welcome"
JavaScript може потім безпосередньо запитувати та маніпулювати цією структурою:
document.querySelector("h1");
По суті, DOM відповідає на одне запитання:
Які елементи існують на сторінці?
5. CSS → CSSOM
Окрім DOM, браузер також аналізує ваш CSS та створює CSSOM, або CSS Object Model.
Наприклад:
h1 {
color: red;
font-size: 30px;
}
На основі цього браузер визначає, які стилі застосовуються до кожного елемента.
Концептуально це виглядає так:
h1
├── color: red
└── font-size: 30px
CSSOM відповідає на інше запитання:
Як слід візуально оформити кожен елемент?
6. DOM + CSSOM → Render Tree
Далі браузер об’єднує DOM та CSSOM для створення дерева відображення.
Є одна нюанс, яку варто зазначити:
Не кожен вузол у DOM потрапляє до дерева відображення.
Наприклад, розгляньмо цей маркування:
<div class="hidden">
Hello
</div>
У поєднанні з цим правилом:
.hidden {
display: none;
}
Елемент div є у DOM:
DOM
└── div
Але оскільки він має:
display: none;
він повністю виключається з дерева візуального відображення.
7. Макетування
Як тільки браузер знає, які елементи потрібно відобразити, він обчислює їхню геометрію.
Це включає розрахунок таких показників, як:
- Ширина
- Висота
- Позиція
- Маржі
- Внутрішні відступи
- Координати
- Як елементи взаємодіють просторово один з одним
Візьмемо це правило як приклад:
.box {
width: 200px;
height: 100px;
margin-left: 50px;
}
Браузер має генерувати конкретні значення у такому форматі:
x = 50px
y = ...
width = 200px
height = 100px
Цей крок обчислень називається Layout, а також часто позначається як Reflow.
8. Paint
Після завершення процесу Layout у браузера є координати кожного видимого елемента.
Наступним кроком є намальування того, як насправді виглядають ці елементи.
Розгляньмо таке форматування:
.box {
background: blue;
color: white;
border: 2px solid black;
}
Процес Paint включає такі елементи:
- Фон
- Текст
- Краї
- Тіні
- Зображення
- Інші візуальні елементи
У послідовності:
Layout
↓
Paint
↓
Pixels
9. Composite
Сучасні браузери можуть розділити сторінку на кілька окремих шарів.
Наприклад:
Layer 1 → Page background
Layer 2 → Header
Layer 3 → Content
Layer 4 → Modal
Потім композитор браузера об’єднує всі ці шари в єдине зображення, яке з’являється на екрані.
Певні властивості CSS, такі як:
transform
opacity
Його зазвичай можна анімувати лише за допомогою композиції, уникаючи складніших операцій з макетування, які вимагають властивостей, що впливають на геометрію.
Це частина причин, чому анімування transform зазвичай є бажаним підходом.
10. Життєвий цикл відображення в React
Тепер, коли ми розглянули сторону браузера, давайте перейдемо до того, як React обробляє відображення.
Корисна модель розуміння ділить цей процес на чотири етапи:
1. Trigger
2. Render
3. Reconciliation
4. Commit
5. Browser Rendering
Розгляньмо кожен з них.
11. Етап 1 — Запуск
Перш ніж щось інше відбудеться, React потребує сигналу, який говорить:
"Інтерфейс може потребувати оновлення."
Цей сигнал називається запуском відображення.
Типові запуски включають:
1. Оновлення стану: setCount(10);
2. Зміна параметрів: <Child count={count} />
Коли батьківський компонент переробляється з новим значенням для count, його дочірні компоненти можуть у результаті отримати нові пропси.
3. Оновлення контексту: useContext(MyContext);
Щоразу, коли змінюється значення, отримане з контексту, компоненти, які його використовують, можуть потребувати оновлення.
4. Оновлення зовнішнього сховища: Бібліотеки та API також можуть спричиняти оновлення React.
Приклади включають:
Redux
Zustand
useSyncExternalStore
Перше завантаження додатку також вважається тригером.
12. Фаза 2 — Рендеринг
Як тільки React отримує інформацію про оновлення, він переходить у фазу рендерингу, де виконує компоненти для формування нового інтерфейсу.
Розгляньмо цей приклад:
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Спочатку count дорівнює 0. React викликає Counter() та отримує:
<button>
Count: 0
</button>
Як тільки натискається кнопка та виконується цей код:
setCount(c => c + 1);
count стає 1, і React перезапускає компонент, що призводить до наступного:
<button>
Count: 1
</button>
13. Важливо: відображення не означає оновлення DOM
Це критична відмінність у моделі React.
Коли React виконує цю функцію:
function Counter() {
console.log("Counter rendered");
return <button>Count: 1</button>;
}
він просто обчислює, яким мало б бути інтерфейс користувача. Це не означає автоматично, що DOM змінився або що браузер щось перемалював.
Справжня послідовність виглядає так:
Component executes
↓
React creates new element representation
↓
Reconciliation
↓
Determine required DOM changes
↓
Commit changes
Це призводить до основного правила:
Відображення компонента не автоматично означає оновлення DOM.
14. Фаза 3 — Узгодження
На цьому етапі React порівнює старий та новий інтерфейси користувача, щоб з’ясувати, що саме змінилося.
Припустимо, попередня структура виглядала так:
button
└── "Count: 0"
А нова структура виглядає так:
button
└── "Count: 1"
React може з’ясувати:
button → same
text → changed
Тож йому потрібно впливати лише на ту частину DOM, яка дійсно змінилася:
button.textContent = "Count: 1";
Головна мета — уникнути розбирання та повторної будови всього дерева DOM, коли змінилася лише невелика його частина.
15. Ключі та узгодження
Ключі мають найвище значення, коли React працює зі списками.
Наприклад:
items.map(item => (
<li key={item.id}>
{item.name}
</li>
))
React покладається на ключі, щоб відстежувати, який елемент є яким під час кожного оновлення. Припустимо, ви починаєте з:
Before:
A
B
C
і закінчуєте з:
After:
A
C
За наявності стабільних ключів React може правильно зробити висновки:
A → same
B → removed
C → same
Це надає React набагато надійнішу інформацію для роботи під час узгодження. Уникайте використання індексів масиву як ключів, коли елементи списку можуть бути переставлені, додані чи вилучені.
16. Етап 4 — Затвердження
Як тільки React визначає, що потрібно змінити, він переходить до фази комітування, де фактично застосовує ці зміни до реального DOM.
На цьому етапі внутрішня структура React та активний DOM нарешті збігаються.
Фаза комітування є окремим кроком від фази відображення:
Render Phase
↓
Calculate what should change
Commit Phase
↓
Actually apply DOM changes
17. Фаза відображення проти фази комітування
Розуміння цього розділення є одним із найпоширеніших питань під час співбесід.
Фаза відображення
Під час цієї фази React:
- Виконує функції компонентів
- Визначає наступну версію інтерфейсу
- Виконує процедуру узгодження
- Визначає, що потрібно змінити
Component
↓
React elements
↓
Reconciliation
Фаза комітування
Під час цієї фази React:
- Застосовує фактичні зміни DOM
- Оновлює всі посилання
- Виконує ефекти макетування, пов’язані з цим оновленням
React result
↓
Real DOM
Ось спрощена схема процесу:
React
│
▼
┌──────────────┐
│ Render Phase │
└──────┬───────┘
│
▼
┌───────────────┐
│ Commit Phase │
└──────┬────────┘
│
▼
Real DOM
18. Що відбувається після коміту?
Як тільки завершується етап коміту, браузер бере керування та помічає, що DOM змінився.
Залежно від того, що змінилося, браузеру може знадобитися пройти кілька етапів:
DOM Changes
↓
Style Calculation
↓
Layout
↓
Paint
↓
Composite
Не кожна зміна вимагає всіх цих етапів для отримання значущого результату — наприклад, іноді достатньо лише оновлення візуального вигляду, без повної переробки макету.
Саме тут власний цикл життя React передає обов’язки браузерській системі відображення, описаній раніше.
19. useLayoutEffect
useLayoutEffect викликається після того, як React здійснив оновлення DOM, але до того, як браузер встигне намалювати новий вигляд.
Послідовність виглядає так:
React Render
↓
Commit
↓
DOM updated
↓
useLayoutEffect
↓
Browser Paint
function Tooltip() {
const ref = useRef(null);
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
console.log(rect.width);
}, []);
return (
<div ref={ref}>
Tooltip
</div>
);
}
Цей хук є правильним інструментом, коли вам потрібно:
- Виміряти елементи в DOM
- Ознайомитися з поточними значеннями макету
- Здійснити візуальні корективи синхронно, ще до того, як користувач щось побачить
Одна застереження: якщо робота всередині useLayoutEffect є обтяженою, це може затримати малювання та спричинити повільну роботу інтерфейсу.
20. useEffect
useEffect, навпаки, призначений для ефектів, які не повинні блокувати процес малювання браузером.
useEffect(() => {
fetch("/api/products");
}, []);
Render
↓
Commit
↓
Browser Paint
↓
useEffect
Фактичні деталі планування є трохи складнішими, ніж це описано, але основне практичне правило полягає у:
Використовуйте
useLayoutEffectлише тоді, коли щось має відбутися перед тим, як браузер намалює інтерфейс; використовуйтеuseEffectдля всього, що може виконуватися після того, як інтерфейс вже став видимим.
21. Повний приклад
import { useEffect, useLayoutEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
console.log("Render");
useLayoutEffect(() => {
console.log("Layout Effect");
});
useEffect(() => {
console.log("Effect");
});
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Концептуально послідовність подій виглядає так:
User clicks button
↓
setCount()
↓
State update scheduled
↓
Counter() executes
↓
"Render"
↓
React reconciles
↓
React commits DOM update
↓
"Layout Effect"
↓
Browser paints updated UI
↓
"Effect"
22. Чи призводить перерендеринг батьківського елемента до перерендерингу дочірнього?
Розглянемо цей приклад:
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<Child />
</>
);
}
Коли оновлюється count:
Parent renders
↓
Child is encountered
↓
Child may render as part of reconciliation
So,
Parent state update
↓
Parent render
↓
Child can render
Ця поведінка є однією з причин існування React.memo як засобу оптимізації.
23. React.memo
Припустимо, у вас є:
const Child = React.memo(function Child() {
console.log("Child rendered");
return <p>Hello</p>;
});
Якщо батьківський елемент перерендерується, але пропси, передані дочірньому елементу, залишаються ідентичними з точки зору посилання, React може повністю пропустити перерендеринг цього мемоїзованого дочірнього елемента.
Ось ця ідея візуально:
Parent update
↓
Parent renders
↓
React.memo checks Child props
↓
Props unchanged
↓
Skip Child render
Проте пам’ятайте:
React.memoпропонує спосіб покращення продуктивності — але не гарантує, що компонент більше ніколи не буде відрендерований.
Зміни контексту, внутрішнього стану та інші фактори все одно можуть змусити його оновитися, незалежно від мемоїзації.
24. useMemo та useCallback
Ці два хуки часто неправильно розуміються.
useMemo: Він мемоїзує обчислений значення.
const result = useMemo(() => expensiveCalculation(data), [data]);
useCallback: Він мемоїзує посилання на функцію.
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
Обидва дійсно корисні у ситуаціях, коли рівність посилань має значення для уникнення повторної роботи — особливо під час передачі пропсів до мемоїзованих дочірніх компонентів.
Проте їх бездумне використання скрізь зазвичай ускладнює код без реальної покращення продуктивності.
25. Отримання результату від React не є тим самим, що й намальування елементів браузером
Цей момент варто чітко зазначити.
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<p>Hello</p>
</div>
);
}
Коли змінюється count:
App renders
↓
New React tree created
↓
Reconciliation
↓
Only count text differs
↓
Commit small DOM update
↓
Browser rendering
React не обов’язково будує цю структуру заново:
<div>
<button>...</button>
<p>Hello</p>
</div>
26. Віртуальний DOM — правильне розуміння концепції
Поширеною твердженням є те, що React використовує Віртуальний DOM для прискорення роботи браузера. Це спрощення.
Насправді React зберігає в пам’яті внутрішній опис інтерфейсу та за допомогою процесу узгодження визначає, що змінилося.
Сам браузер не має поняття про Віртуальний DOM. Він працює лише з такими елементами, як HTML, DOM, CSS та, зрештою, пікселі.
Концептуально процес виглядає так:
React Component
↓
React Element Tree
↓
Reconciliation
↓
DOM Mutations
↓
Browser
↓
Pixels
Віртуальний DOM — це те, що браузер ніколи не відображає насправді.
27. Перевідображення vs Перемальовування
Це два різні концепти, які не слід плутати.
Перевідображення в React
Це тоді, коли функція компонента виконується знову — по суті, виконується Component().
Перемальовування браузером
Це тоді, коли браузер перемальовує пікселі на екрані через зміну якоїсь візуальної властивості — це крок Paint.
Тож пам’ятайте: перевідображення в React не є еквівалентним перемальовуванню браузером.
Цілком можливо, що компонент буде перевідображатися знову, проте залишиться та сама структура DOM. Навпаки, браузер може перемальовуватися з причин, які не мають жодного відношення до відображення компонентів React.
28. Перерозподіл форматування vs Перемальовування
Візьмемо такий приклад: element.style.width = „500px“;
Зміна ширини елемента зазвичай змушує перерахувати макет:
Style
↓
Layout
↓
Paint
↓
Composite
Тепер порівняйте це з: element.style.backgroundColor = „red“;
У цьому випадку браузер зазвичай не потребує повторного виконання обчислень геометрії:
Style
↓
Paint
↓
Composite
А для певних змін трансформацій чи прозорості браузер може потребувати лише:
Composite
Розуміння цієї різниці дуже важливе під час оптимізації анімацій чи інтерфейсів з високими вимогами до відображення.
29. Застосування цього до реальної ситуації в React
Product Page
├── Navbar
├── Product Gallery
├── Product Information
├── Add To Cart
├── Reviews
└── Recommendations
Припустимо, користувач натискає кнопку Додати до кошика, що оновлює кількість товарів у кошику.
Концептуально послідовність подій може виглядати так:
User clicks "Add to Cart"
↓
Cart state updates
↓
React schedules update
↓
Relevant components render
↓
React reconciles new tree
↓
Only required DOM changes are committed
↓
Browser performs necessary rendering work
↓
Updated cart count appears
У добре структурованому додатку оновлення кількості товарів у кошику не повинно змушувати до непотрібної перебудови всього інтерфейсу. Саме тому рішення щодо меж компонентів, місця зберігання стану, методів мемоїзації та загальної архітектури відображення мають велике значення.
30. Коли дороге відображення стає проблемою продуктивності
function ProductList({ products }) {
return products.map(product => (
<ExpensiveProductCard
key={product.id}
product={product}
/>
));
}
Припустимо, цей список відображає 10 000 товарів, і якась не пов’язана з ними зміна стану спричиняє зайву роботу великої кількості компонентів — продуктивність може суттєво погіршитися.
Поширені методи зменшення цього впливу включають:
React.memo
+
Stable props
+
useMemo/useCallback where justified
+
Virtualization
+
Pagination
+
Code splitting
Для особливо великих списків віртуалізація зазвичай є особливо ефективною:
10000 items
↓
Only visible ~20-50 items rendered
31. Як відображення взаємодіє з конкурентним React
Новіші версії React мають більш розвинені можливості планування. React може надавати пріоритет різним оновленням та, коли це доцільно, призупиняти або відкладати роботу з відображенням.
Ключова концепція, яку потрібно пам’ятати:
Render work
↓
Can be scheduled
↓
Can be interrupted in modern React
↓
Eventually committed
Саме тому побічні ефекти не повинні знаходитися всередині логіки відображення компонента:
function Component() {
fetch("/api/data"); // ❌ Side effect during render
return <div>Hello</div>;
}
Натомість використовуйте хук effect або відповідну схему отримання даних. Ідеально, якби фаза відображення залишалася чистою.
32. Аргументи на користь чистого відображення
Концептуально компонент React повинен поводитися як чиста функція:
UI = f(props, state)
При однакових props та стані він завжди має описувати один і той самий інтерфейс. Наприклад:
function Greeting({ name }) {
return <h1>Hello {name}</h1>;
}
Така поведінка є передбачуваною та безпечною.
Навпаки, виконання подібних дій всередині функції відображення є проблематичним:
function Component() {
localStorage.setItem("x", "1"); // ❌
fetch("/api"); // ❌
return <div>Hello</div>;
}
Це вводить побічні ефекти у фазу, яку React може виконати більше разів або запланувати непередбачуваними способами.
Побічні ефекти мають знаходитися в іншому місці — у функціях ефектів, обробниках подій чи механізмах отримання даних, які надає ваша фреймворк-система.
33. Життєвий цикл відображення — повна когнітивна модель
USER ACTION
│
▼
State / Props Update
│
▼
React Schedules Work
│
▼
┌──────────────┐
│ RENDER │
│ │
│ Components │
│ execute │
└──────┬───────┘
│
▼
Reconciliation
│
▼
┌──────────────┐
│ COMMIT │
│ │
│ DOM mutation │
└──────┬───────┘
│
▼
useLayoutEffect
│
▼
Browser Rendering
│
▼
Style Calculation
│
▼
Layout
│
▼
Paint
│
▼
Composite
│
▼
SCREEN
│
▼
useEffect
Поширені запитання на співбесіду
Q1. Чи оновлює оновлення стану DOM негайно?
Ні. Оновлення стану лише планує виконання завдань для React. React відображає компонент, порівнює отриманий результат із попереднім та лише потім вносить необхідні зміни до DOM.
setState
↓
Render
↓
Reconciliation
↓
Commit
↓
DOM
Q2. Чи призводить кожне відображення до оновлення DOM?
Ні. React може відобразити компонент та все одно дійти висновку, що отриманий опис інтерфейсу ідентичний тому, що вже є на екрані, у такому разі він пропускає зміни DOM.
Render
↓
Compare
↓
No meaningful DOM difference
↓
No DOM mutation
Q3. Чи змушує повторне відрендерування в React браузер переробити малювання?
Ні. Відрендерування в React та малювання в браузері — це два незалежні процеси.
React Render ≠ Browser Paint
Q4. Що означає приведення до узгодженості?
Приведення до узгодженості — це процес, під час якого React порівнює щойно обчислений дерево UI з попереднім, щоб визначити, що саме потрібно змінити.
Q5. Що відбувається на етапі commit?
Під час commit React застосовує необхідні зміни до середовища хоста — наприклад, оновлює справжні вузли DOM у браузері.
Q6. У чому різниця між useEffect та useLayoutEffect?
Простий спосіб запам’ятати це:
useLayoutEffect
→ after DOM commit, before paint
useEffect
→ after commit and generally after paint
Використовуйте useLayoutEffect, коли вам потрібно синхронно виміряти або скоригувати макет до того, як користувач побачить новий кадр. Для більшості побічних ефектів, які не потребують блокування процесу відображення, використовуйте useEffect.
Q7. Що змушує компонент відобразитися?
Типовими факторами є: початкове завантаження компонента, оновлення стану, переробка компонента-батька, зміна параметрів, оновлення контексту або оновлення з зовнішнього сховища даних.
Q8. Як можна оптимізувати процес відображення? [IMP]
Замість того, щоб за замовчуванням використовувати useMemo скрізь, спочатку визначте місце справжнього узгоджувального елемента. Потім розгляньте такі підходи:
1. Тримайте стан близько до місця його використання
Уникайте структури, у якій стан знаходиться нагорі, а кожен нащадок залежить від нього:
App
↓
Everything depends on state
prefer:
Feature
↓
Local state
Краще обирати вужче розміщення, коли глобальний стан насправді не є необхідним.
2. Вибірково мемоїзувати дорогі компоненти
const ProductCard = React.memo(ProductCardComponent);
3. Слідкуйте за створенням об’єктів, яке порушує мемоїзацію
Замість того, щоб постійно створювати новий об’єкт безпосередньо в коді, як це:
<Child config={{ theme: "dark" }} />
Вам може знадобитися стабільний посилання, якщо Child обгорнутий у React.memo та ідентичність має значення — але не вирішуйте цю проблему, доки вона справді не стане проблемою.
4. Віртуалізація довгих списків
Замість того, щоб відображати всі 10 000 елементів DOM одночасно, відображайте лише ті, що зараз видимі.
5. Відкладіть завантаження дорогих функцій
const AdminDashboard = lazy(() => import("./AdminDashboard"));
Це зменшує кількість JavaScript-коду, який потрібно завантажувати та виконувати заздалегідь.
6. Використовуйте техніки відображення, придатні для браузерів
У міру можливості використовуйте недорогі ефекти анімації, такі як transform та opacity, і уникайте примусового перерахунку складних макетів на кожному кадрі.