Избегайте больших и сложных макетов.

Макет — это то место, где браузер определяет геометрическую информацию для элементов — их размер и расположение на странице. Каждый элемент будет иметь явную или неявную информацию о размере на основе использованного CSS, содержимого элемента или родительского элемента. Этот процесс называется «Макет» в Chrome.

Джереми Вагнер
Джереми Вагнер
Пол Льюис

Макет — это то, где браузер определяет геометрическую информацию для элементов: их размер и расположение на странице. Каждый элемент будет иметь явную или неявную информацию о размере на основе использованного CSS, содержимого элемента или родительского элемента. Этот процесс называется «Макет» в Chrome (и производных браузерах, таких как Edge) и Safari. В Firefox это называется Reflow, но процесс практически тот же.

Как и при расчете стиля, первоочередными проблемами стоимости макета являются:

  1. Количество элементов, для которых требуется макет, который зависит от размера DOM страницы.
  2. Сложность этих планировок.

Краткое содержание

  • Макет напрямую влияет на задержку взаимодействия.
  • Макет обычно распространяется на весь документ.
  • Количество элементов DOM повлияет на производительность; вам следует избегать запуска макета, где это возможно.
  • Избегайте принудительной синхронной разметки и ее перестановки; прочитайте значения стиля, а затем внесите изменения в стиль.

Влияние макета на задержку взаимодействия

Когда пользователь взаимодействует со страницей, это взаимодействие должно быть максимально быстрым. Время, необходимое для завершения взаимодействия (заканчивающееся, когда браузер представляет следующий кадр для отображения результатов взаимодействия), называется задержкой взаимодействия . Это аспект производительности страницы, который измеряется метрикой «Взаимодействие с следующей отрисовкой» .

Время, необходимое браузеру для представления следующего кадра в ответ на взаимодействие с пользователем, называется задержкой представления взаимодействия. Цель взаимодействия — предоставить визуальную обратную связь, чтобы сигнализировать пользователю о том, что что-то произошло, а визуальные обновления могут включать в себя определенный объем работы по макету для достижения этой цели.

Чтобы сохранить INP вашего веб-сайта как можно более низким, важно по возможности избегать макета. Если невозможно полностью избежать макетирования, важно ограничить работу с макетом, чтобы браузер мог быстро отображать следующий кадр.

Избегайте макетов везде, где это возможно

Когда вы меняете стили, браузер проверяет, требуют ли какие-либо изменения расчета макета и обновления этого дерева рендеринга. Изменения «геометрических свойств», таких как ширина, высота, лево или верх, требуют макета.

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

Макет почти всегда распространяется на весь документ. Если у вас много элементов, потребуется много времени, чтобы определить их расположение и размеры.

Если избежать макета невозможно, то ключевым моментом является еще раз использовать Chrome DevTools, чтобы увидеть, сколько времени это занимает, и определить, является ли макет причиной узкого места. Сначала откройте DevTools, перейдите на вкладку «Временная шкала», нажмите «Запись» и взаимодействуйте со своим сайтом. Когда вы остановите запись, вы увидите детализацию эффективности вашего сайта:

DevTools долгое время отображается в макете.

Изучив трассировку в приведенном выше примере, мы видим, что внутри макета для каждого кадра тратится более 28 миллисекунд, что, если у нас есть 16 миллисекунд для вывода кадра на экран в анимации, это слишком много. Вы также можете видеть, что DevTools сообщит вам размер дерева (в данном случае 1618 элементов) и количество узлов, нуждающихся в макете (в данном случае 5).

Имейте в виду, что общий совет здесь — по возможности избегать макета, но избежать макета не всегда возможно. В тех случаях, когда вы не можете избежать макета, знайте, что стоимость макета зависит от размера DOM. Хотя взаимосвязь между ними не является тесно связанной, более крупные модели DOM обычно требуют более высоких затрат на макетирование.

Избегайте принудительной синхронной компоновки

Доставка кадра на экран имеет следующий порядок:

Использование флексбокса в качестве макета.

Сначала запускается JavaScript, затем выполняются вычисления стилей, а затем макет. Однако можно заставить браузер выполнить разметку раньше с помощью JavaScript. Это называется принудительной синхронной компоновкой .

Первое, что нужно иметь в виду, это то, что при запуске JavaScript все старые значения макета из предыдущего кадра известны и доступны для запроса. Итак, если, например, вы хотите записать высоту элемента (назовем его «коробкой») в начале кадра, вы можете написать такой код:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Ситуация становится проблематичной, если вы изменили стили поля до того, как запросили его высоту:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Теперь, чтобы ответить на вопрос о высоте, браузер должен сначала применить изменение стиля (из-за добавления super-big класса), а затем запустить макет. Только тогда он сможет вернуть правильную высоту. Это ненужная и потенциально дорогостоящая работа.

По этой причине вам всегда следует группировать чтение стилей и выполнять их сначала (когда браузер может использовать значения макета предыдущего кадра), а затем выполнять любые записи:

Если все сделано правильно, вышеуказанная функция будет такой:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

По большей части вам не нужно применять стили, а затем запрашивать значения; использования значений последнего кадра должно быть достаточно. Выполнение вычислений стиля и макета синхронно и раньше, чем хотелось бы браузеру, является потенциальными узкими местами, и это не то, что вам обычно хочется делать.

Избегайте беспорядка в макете

Есть способ сделать принудительные синхронные макеты еще хуже: выполнять их множество в быстрой последовательности . Взгляните на этот код:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

Этот код перебирает группу абзацев и устанавливает ширину каждого абзаца в соответствии с шириной элемента под названием «box». Это выглядит достаточно безобидно, но проблема в том, что каждая итерация цикла считывает значение стиля ( box.offsetWidth ), а затем немедленно использует его для обновления ширины абзаца ( paragraphs[i].style.width ). На следующей итерации цикла браузер должен учитывать тот факт, что стили изменились с момента последнего запроса offsetWidth (на предыдущей итерации), поэтому он должен применить изменения стиля и запустить макет. Это будет происходить на каждой итерации! .

Исправление для этого примера состоит в том, чтобы еще раз прочитать , а затем записать значения:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

Если вы хотите гарантировать безопасность, рассмотрите возможность использования FastDOM , который автоматически группирует операции чтения и записи и не позволяет вам случайно запускать принудительные синхронные макеты или случайное сбой макета.

Героическое изображение из Unsplash , автор Хэл Гейтвуд .