Batch DOM updates

Batch DOM updates — техника группировки множества изменений DOM в одну операцию для минимизации reflow и repaint, критически влияющих на производительность страницы.

Зачем нужно

Каждое отдельное изменение DOM (особенно чтение/запись геометрических свойств вперемешку) вызывает принудительный reflow — браузер пересчитывает расположение всех элементов. При 100+ изменениях в цикле это приводит к «Layout Thrashing» и зависанию интерфейса. Группировка изменений в один батч позволяет браузеру выполнить один reflow вместо 100.

Где используется

  • Рендер длинных списков: добавление сотен элементов в DOM
  • Анимации через JavaScript: обновление позиций/размеров в requestAnimationFrame
  • Таблицы с динамическими данными: пересчёт ширины столбцов
  • Виртуализация: замена видимых строк при скролле
  • Обновление множества стилей после сетевого запроса

Основной контент

Проблема: Layout Thrashing

// ПЛОХО: чтение и запись вперемешку — каждая пара вызывает reflow
const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
  const height = box.offsetHeight; // чтение — браузер вынужден reflow
  box.style.height = height * 2 + 'px'; // запись
  // на следующей итерации offsetHeight снова вызовет reflow!
});

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

DocumentFragment для вставки множества элементов

const list = document.getElementById('list');

// ПЛОХО: каждый appendChild вызывает reflow
const items = Array.from({ length: 1000 }, (_, i) => i);
items.forEach(i => {
  const li = document.createElement('li');
  li.textContent = `Элемент ${i}`;
  list.appendChild(li); // 1000 reflow!
});

// ХОРОШО: DocumentFragment — вставка за один раз
const fragment = document.createDocumentFragment;
items.forEach(i => {
  const li = document.createElement('li');
  li.textContent = `Элемент ${i}`;
  fragment.appendChild(li); // без reflow
});
list.appendChild(fragment); // один reflow

innerHTML для массовой замены

// Для полной замены содержимого innerHTML эффективнее множества appendChild
function renderItems(container, items) {
  // Строим HTML-строку, затем вставляем за раз
  container.innerHTML = items
    .map(item => `<li class="item" data-id="${item.id}">${item.name}</li>`)
    .join('');
}

// Для частичных обновлений лучше сохранять ссылки на элементы
// и менять только textContent / className

requestAnimationFrame для анимаций

// Группируем все изменения стилей внутри rAF
function animateElements(elements, progress) {
  // Читаем ВНЕ rAF, если нужно
  requestAnimationFrame(() => {
    // Пишем ВНУТРИ rAF — браузер объединит с рендером
    elements.forEach((el, i) => {
      el.style.transform = `translateX(${progress * i}px)`;
    });
  });
}

// FastDOM-паттерн: явное разделение reads и writes
const reads = ;
const writes = ;

function measure(fn) { reads.push(fn); }
function mutate(fn) { writes.push(fn); }

function flush() {
  reads.forEach(fn => fn);
  reads.length = 0;
  writes.forEach(fn => fn);
  writes.length = 0;
}

requestAnimationFrame(flush);

classList вместо style

// ПЛОХО: несколько style-изменений
el.style.color = 'red';
el.style.fontWeight = 'bold';
el.style.fontSize = '16px';

// ХОРОШО: один toggle класса — одно изменение
el.classList.add('highlighted'); // все стили в CSS
// или если несколько классов
el.classList.remove('hidden', 'inactive');
el.classList.add('visible', 'active');

Частые ошибки

  • Чтение offsetHeight/width внутри цикла записи: это самая распространённая причина Layout Thrashing — разносите чтения и записи.
  • Модификация DOM вне rAF в анимациях: изменения стилей вне requestAnimationFrame могут вызвать лишние перерисовки или вообще пропустить кадр.
  • innerHTML для частичных обновлений: innerHTML = ... сбрасывает все обработчики событий на дочерних элементах — используйте только для полной замены.
  • Не использовать DocumentFragment: при вставке 50+ элементов отсутствие Fragment заметно влияет на производительность.

Связанные темы

Ресурсы