Learning Book

Repaint, Reflow и батчинг

Repaint и Reflow

Что такое Reflow (Layout)

Reflow — пересчёт геометрии страницы. Запускается при изменениях:

  • Размеров элементов (width, height, font-size)
  • Позиционирования (top, left, margin, padding)
  • Добавлении/удалении DOM-элементов
  • Изменении display, visibility
  • Изменении шрифтов, текста

Что вызывает Forced Reflow

// Чтение следующих свойств ПОСЛЕ изменений DOM
// вызывает принудительный reflow:
el.offsetWidth / el.offsetHeight
el.offsetTop / el.offsetLeft
el.scrollTop / el.scrollLeft / el.scrollWidth
el.clientWidth / el.clientHeight
el.getBoundingClientRect()
window.getComputedStyle(el)

Layout Thrashing — главный враг производительности

// ПЛОХО: чередуем чтение и запись — N forced reflows
elements.forEach(el => {
  const height = el.offsetHeight   // forced reflow (чтение)
  el.style.height = height * 2 + 'px'  // изменение (запись)
  // на следующей итерации чтение снова вызовет reflow!
})

// ХОРОШО: разделяем чтение и запись
const heights = Array.from(elements, el => el.offsetHeight)  // все чтения
elements.forEach((el, i) => {
  el.style.height = heights[i] * 2 + 'px'  // все записи
})

Техники минимизации

// 1. Скрыть → изменить → показать
el.style.display = 'none'      // убрать из rendering
el.style.width = '200px'       // N изменений — нет reflow
el.style.height = '100px'
el.style.margin = '10px'
el.style.display = 'block'     // показать — один reflow

// 2. Клонировать → изменить → заменить
const clone = el.cloneNode(true)
clone.style.width = '200px'    // изменения на клоне
clone.style.height = '100px'
el.parentNode.replaceChild(clone, el)  // один reflow

// 3. CSS классы вместо прямых стилей
el.classList.add('expanded')   // добавить весь набор стилей за один reflow
// вместо:
el.style.width = '300px'
el.style.height = '200px'
el.style.padding = '20px'
Правило: сначала все операции ЧТЕНИЯ, потом все операции ЗАПИСИ. Никогда не чередуйте. Используйте fastdom или requestAnimationFrame для правильного планирования.

Repaint (без Reflow)

Repaint запускается когда меняются визуальные свойства без изменения геометрии:

// Только repaint (без reflow):
el.style.color = 'red'
el.style.backgroundColor = '#fff'
el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'
el.style.outline = '2px solid blue'

// Без repaint и reflow (только composite):
el.style.transform = 'scale(1.1)'
el.style.opacity = '0.5'