← Назад к вопросам
Можно ли сделать больше reflow в один кадр?
2.0 Middle🔥 121 комментариев
#Браузер и сетевые технологии#Оптимизация и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Можно ли сделать больше reflow в один кадр?
Да, можно! Вопрос основан на неполном понимании браузерного рендеринга. Разберёмся подробно, как работает reflow, когда он происходит и как оптимизировать его.
Что такое Reflow и Repaint
Reflow (перерасчёт раскладки):
- Когда браузер пересчитывает размеры и позиции элементов
- Самая дорогая операция (может занять десятки миллисекунд)
- Затрагивает производительность
Repaint (перекраска):
- Когда браузер перерисовывает элемент (цвет, тень и т.д.)
- Дешевле чем reflow, но всё равно дорогая
Браузерный цикл рендеринга
Браузер работает примерно так (60 FPS = один кадр в 16.67ms):
16.67ms (один кадр на 60 FPS)
├─ JavaScript ─────────────────
├─ Recalculate Styles ─────────
├─ Layout (Reflow) ────────────
├─ Paint (Repaint) ────────────
├─ Composite ───────────────────
└─ Display
Пример: Когда происходит Reflow
// Это вызовет reflow:
1. изменение ширины/высоты (width, height)
2. изменение padding/margin/border
3. изменение позиции (top, left, position)
4. чтение свойств раскладки (offsetWidth, clientHeight, getBoundingClientRect())
5. добавление/удаление элементов из DOM
6. изменение display, visibility
7. изменение font-size (может изменить высоту текста)
8. orientation change
9. scroll
❌ Плохой пример: Много Reflows
// Каждая строка может вызвать reflow!
const box = document.getElementById('box');
// Reflow 1: читаем offsetWidth
const width = box.offsetWidth;
// Reflow 2: устанавливаем новую ширину
box.style.width = (width + 10) + 'px'; // reflow!
// Reflow 3: читаем offsetHeight
const height = box.offsetHeight;
// Reflow 4: устанавливаем новую высоту
box.style.height = (height + 20) + 'px'; // reflow!
// Reflow 5: читаем clientTop
const top = box.clientTop;
// Reflow 6: устанавливаем margin
box.style.marginTop = (top + 5) + 'px'; // reflow!
// ИТОГО: минимум 6 reflows в один кадр!
✅ Хороший пример: Батчинг (Batching)
Группируем чтение, потом группируем запись:
const box = document.getElementById('box');
// Шаг 1: Читаем ВСЕ свойства (один reflow)
const width = box.offsetWidth;
const height = box.offsetHeight;
const top = box.clientTop;
// Шаг 2: Пишем ВСЕ свойства (один reflow)
box.style.width = (width + 10) + 'px';
box.style.height = (height + 20) + 'px';
box.style.marginTop = (top + 5) + 'px';
// ИТОГО: 2 reflows вместо 6!
Почему это работает
Браузер оптимизирует reflows:
┌─ JavaScript код выполняется
│ ├─ Читаем offsetWidth → браузер: "нужно сделать reflow"
│ ├─ Читаем offsetHeight → браузер: "уже запланирован"
│ ├─ Читаем clientTop → браузер: "уже запланирован"
│ ├─ Пишем width → браузер: "добавим в очередь"
│ ├─ Пишем height → браузер: "добавим в очередь"
│ ├─ Пишем marginTop → браузер: "добавим в очередь"
│ └─ Код закончился
├─ Браузер: Выполняю один reflow с учётом всех изменений
└─ Браузер: Выполняю paint
FastDOM паттерн
Лучшая практика — это паттерн Fastdom:
// Разделяем читание и запись
const items = document.querySelectorAll('.item');
const measurements = [];
// Фаза 1: ВСЕ чтения
for (const item of items) {
measurements.push({
element: item,
width: item.offsetWidth,
height: item.offsetHeight
});
}
// Фаза 2: ВСЕ записи
for (const m of measurements) {
m.element.style.width = (m.width * 1.5) + 'px';
m.element.style.height = (m.height * 1.5) + 'px';
}
Реальный пример: Анимация 1000 элементов
// ❌ Плохо: 1000 reflows
for (let i = 0; i < 1000; i++) {
const elem = document.getElementById(`item-${i}`);
elem.style.transform = `translateX(${Math.random() * 100}px)`; // reflow!
}
// ✅ Хорошо: 1 reflow (или 0 если использовать transform)
for (let i = 0; i < 1000; i++) {
const elem = document.getElementById(`item-${i}`);
// transform не вызывает reflow, только repaint!
elem.style.transform = `translateX(${Math.random() * 100}px)`;
}
Свойства, которые НЕ вызывают Reflow
Эти свойства вызывают только repaint (дешевле):
// Не вызывают reflow:
elem.style.color = 'red'; // repaint
elem.style.backgroundColor = '#f0f'; // repaint
elem.style.boxShadow = '...'; // repaint
elem.style.transform = 'rotate(45deg)'; // repaint только
elem.style.opacity = 0.5; // repaint только
// Вызывают reflow:
elem.style.width = '100px'; // reflow!
elem.style.height = '50px'; // reflow!
elem.style.top = '10px'; // reflow!
elem.style.padding = '10px'; // reflow!
requestAnimationFrame (RAF)
Для анимаций используй RAF, он синхронизирован с браузерным циклом:
// ❌ Может вызвать jank
let x = 0;
function animate() {
x += 5;
box.style.left = x + 'px'; // Может быть вне sync с браузером
setTimeout(animate, 16); // не точно 16ms
}
// ✅ Правильно: один reflow на кадр
function animate() {
x += 5;
box.style.left = x + 'px'; // Точный момент перед paint
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Инструменты для отладки
DevTools Performance:
// Откроем Performance tab в Chrome DevTools
// Кликнем Record
const button = document.querySelector('button');
button.addEventListener('click', () => {
// Плохой код (много reflows)
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
document.body.appendChild(div);
console.log(div.offsetHeight); // reflow!
}
});
// Остановим запись и увидим reflows
Paint Timing API:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Paint:', entry.name, entry.startTime);
// first-paint, first-contentful-paint
}
});
observer.observe({ entryTypes: ['paint'] });
Ответ на вопрос
Да, можно сделать больше reflow в один кадр:
- Браузер не ограничивает количество reflows в один кадр
- Можно сделать 10, 100, 1000 reflows в один кадр
- Но это плохо для производительности!
- Оптимизация: батчинг (группировка чтений и записей)
- Ещё лучше: использовать свойства, которые не вызывают reflow (transform, opacity)
Чеклист оптимизации
✓ Группируй чтения (read) и потом записи (write)
✓ Используй transform вместо top/left/width/height
✓ Используй opacity вместо visibility для скрытия
✓ Батчируй изменения в один style.cssText
✓ Используй requestAnimationFrame для анимаций
✓ Удаляй элементы из DOM перед изменениями
✓ Используй will-change для подсказки браузеру
✓ Проверяй Paint Timing в DevTools