← Назад к вопросам

Можно ли сделать больше 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 в один кадр:

  1. Браузер не ограничивает количество reflows в один кадр
  2. Можно сделать 10, 100, 1000 reflows в один кадр
  3. Но это плохо для производительности!
  4. Оптимизация: батчинг (группировка чтений и записей)
  5. Ещё лучше: использовать свойства, которые не вызывают reflow (transform, opacity)

Чеклист оптимизации

✓ Группируй чтения (read) и потом записи (write)
✓ Используй transform вместо top/left/width/height
✓ Используй opacity вместо visibility для скрытия
✓ Батчируй изменения в один style.cssText
✓ Используй requestAnimationFrame для анимаций
✓ Удаляй элементы из DOM перед изменениями
✓ Используй will-change для подсказки браузеру
✓ Проверяй Paint Timing в DevTools
Можно ли сделать больше reflow в один кадр? | PrepBro