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

Что такое Force layout?

2.2 Middle🔥 101 комментариев
#Другое#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Force layout

Force layout (принудительная разметка) — это нежелательное поведение браузера, когда произойдет принудительный recalc стилей и перерисовка страницы из-за обращения к макет-зависимым свойствам в JavaScript. Force layout замораживает JavaScript выполнение, значительно замедляя производительность приложения.

Почему это происходит

Браузер использует асинхронный подход для выполнения изменений:

  1. JavaScript изменяет стили (например, element.style.width = "100px")
  2. Браузер добавляет эти изменения в очередь
  3. Обычно браузер применяет все изменения разом на следующем фрейме

Но если JavaScript обращается к свойству, которое зависит от макета (например, element.offsetWidth), браузер вынужден немедленно:

  1. Пересчитать стили (recalc)
  2. Пересчитать макет (layout reflow)
  3. Перерисовать (repaint)

Это вызывает блокировку JavaScript и может привести к потере кадров.

Свойства, которые вызывают Force Layout

Свойства размеров и позиций

// Все эти свойства вызывают force layout:
const width = element.offsetWidth;      // Ширина с padding/border
const height = element.offsetHeight;    // Высота с padding/border
const left = element.offsetLeft;        // Позиция слева
const top = element.offsetTop;          // Позиция сверху
const parent = element.offsetParent;    // Родитель для позиционирования

const scrollWidth = element.scrollWidth;   // Ширина скролла
const scrollHeight = element.scrollHeight; // Высота скролла
const scrollTop = element.scrollTop;       // Текущая позиция скролла
const scrollLeft = element.scrollLeft;     // Позиция скролла слева

Методы

// Методы getBoundingClientRect и др.
const rect = element.getBoundingClientRect(); // FORCE LAYOUT!
const coords = element.getClientRects();      // FORCE LAYOUT!

Вычисленные стили

// Получение вычисленных стилей
const style = window.getComputedStyle(element);
const display = style.display;      // FORCE LAYOUT!
const width = style.width;          // FORCE LAYOUT!

Свойства Window

const width = window.innerWidth;     // FORCE LAYOUT!
const height = window.innerHeight;  // FORCE LAYOUT!
const scrollY = window.scrollY;      // FORCE LAYOUT!

Классический пример проблемы

// ПЛОХО: Force layout в цикле
const elements = document.querySelectorAll(".item");

for (let i = 0; i < elements.length; i++) {
  elements[i].style.width = (100 + i * 10) + "px"; // Добавляем в очередь
  const width = elements[i].offsetWidth;            // FORCE LAYOUT!
  console.log(`Элемент ${i}: ${width}px`);
}

// Результат: N force layouts для N элементов (ОЧЕНЬ МЕДЛЕННО)

Решение 1: Разделить чтение и запись

// ХОРОШО: Сначала читаем, потом пишем
const elements = document.querySelectorAll(".item");
const widths = [];

// Этап 1: Читаем все значения
for (let i = 0; i < elements.length; i++) {
  widths.push(elements[i].offsetWidth); // Один force layout
}

// Этап 2: Пишем значения (без force layout)
for (let i = 0; i < elements.length; i++) {
  elements[i].style.width = (widths[i] + 10) + "px";
}

Решение 2: Использовать requestAnimationFrame

// Использовать rAF для группировки операций
const elements = document.querySelectorAll(".item");

requestAnimationFrame(() => {
  // Все чтение
  const widths = elements.map(el => el.offsetWidth);
  
  // Браузер обработает рAF после измерения макета
  requestAnimationFrame(() => {
    // Все письмо
    widths.forEach((w, i) => {
      elements[i].style.width = (w + 10) + "px";
    });
  });
});

Решение 3: Использовать ResizeObserver или IntersectionObserver

// Для отслеживания изменений размера без force layout
const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    console.log("Размер изменился:", entry.contentRect);
    // Применяем изменения
  }
});

observer.observe(element);

Практический пример в React

// ПЛОХО
function Component() {
  const [positions, setPositions] = React.useState([]);
  
  React.useEffect(() => {
    const elements = document.querySelectorAll(".item");
    const newPositions = [];
    
    for (const el of elements) {
      newPositions.push({
        x: el.offsetLeft,   // FORCE LAYOUT в цикле!
        y: el.offsetTop,    // FORCE LAYOUT в цикле!
      });
    }
    setPositions(newPositions);
  }, []);
}

// ХОРОШО
function Component() {
  const [positions, setPositions] = React.useState([]);
  
  React.useEffect(() => {
    const elements = document.querySelectorAll(".item");
    
    // Один force layout для всех элементов
    const newPositions = Array.from(elements).map(el => ({
      x: el.offsetLeft,
      y: el.offsetTop,
    }));
    
    setPositions(newPositions);
  }, []);
}

Инструменты для диагностики

Chrome DevTools Performance

  1. Открыть DevTools (F12)
  2. Перейти на вкладку Performance
  3. Записать профиль
  4. Поискать "Recalculate Style" и "Layout" в временной шкале
  5. Если видите много коротких блоков, это force layout

Более конкретно

// Можно добавить логирование
function getOffsetsWithLogging(element) {
  console.time("getOffsets");
  const rect = element.getBoundingClientRect();
  console.timeEnd("getOffsets");
  return rect;
}

Когда Force Layout приемлем

  • При инициализации страницы (не в цикле анимации)
  • Редко (не в requestAnimationFrame)
  • Когда производительность не критична
  • При работе с одним элементом

Когда избегать

  • В цикле анимации (requestAnimationFrame)
  • В цикле обработки множества элементов
  • Если нужно 60 FPS (16ms на фрейм)
  • При скроллировании
  • При изменении размера окна

Практический совет для фронтенда

// Батчинг операций с DOM
function batchDOMOperations(operations) {
  // Этап 1: Читаем
  const reads = operations.map(op => op.read());
  
  // Этап 2: Пишем
  operations.forEach((op, i) => op.write(reads[i]));
}

// Использование
batchDOMOperations([
  {
    read: () => element.offsetWidth,
    write: (width) => {
      element.style.transform = `scaleX(${width / 100})`;
    }
  }
]);

Заключение

Force layout — это один из главных врагов производительности веб-приложений. Понимание того, какие операции его вызывают, и как его избежать, критически важно для написания быстрого кода. Основной принцип: читай сразу всё, потом пиши сразу всё.

Что такое Force layout? | PrepBro