Repaint и Reflow: 7 проверенных способов оптимизации в браузере
Перейти

Repaint и Reflow: 7 проверенных способов оптимизации в браузере

#Разное  
Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики веб-приложений и сайтов
  • Фронтенд-инженеры, стремящиеся к повышению производительности интерфейсов
  • Специалисты по оптимизации пользовательского опыта (UX)

Каждый миллисекундный лаг при скролле страницы или анимации — это потенциально потерянный пользователь. Сложные веб-интерфейсы часто становятся жертвами собственной красоты, когда медленная отрисовка превращает плавный UX в слайд-шоу. За кулисами этой драмы скрываются два главных виновника — repaint и reflow. Именно они определяют, будет ваш сайт летать или еле ползти. Но что если я скажу, что вы можете заставить эти процессы работать по вашим правилам? Погрузимся в мир рендеринга и выясним, как превратить тяжеловесные страницы в стремительные интерфейсы с помощью 7 проверенных техник, меняющих правила игры. 🚀

Что такое Repaint и Reflow: разбираем процесс рендеринга

Чтобы эффективно оптимизировать производительность веб-страниц, необходимо понимать, как браузер превращает HTML, CSS и JavaScript в пиксели на экране. Процесс рендеринга в браузере включает несколько критических этапов, среди которых reflow и repaint занимают ключевые позиции.

Когда браузер загружает веб-страницу, он выполняет следующую последовательность операций:

  1. Парсинг HTML — преобразование HTML-разметки в DOM-дерево
  2. Парсинг CSS — создание CSSOM (CSS Object Model)
  3. Комбинирование DOM и CSSOM в дерево рендеринга (Render Tree)
  4. Layout/Reflow — вычисление размеров и положения всех элементов
  5. Paint/Repaint — отрисовка пикселей на экране
  6. Compositing — соединение слоёв для финальной визуализации

Теперь разберём ключевые понятия более детально:

Reflow (перекомпоновка) происходит, когда браузер пересчитывает размеры и положение элементов на странице. Это вычислительно дорогая операция, поскольку изменение одного элемента может повлиять на положение множества других. Reflow запускается при:

  • Изменении размеров окна браузера
  • Изменении размеров элемента или его содержимого
  • Добавлении/удалении элементов из DOM
  • Изменении шрифта, его размера или стиля
  • Изменении позиции (margin, padding, border, position, top, left и т.д.)

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

  • Цвета элемента
  • Фона
  • Видимости (visibility, opacity)
  • Теней
  • Контуров
Характеристика Reflow Repaint
Влияние на производительность Высокое Среднее
Что затрагивает Геометрию элементов (размеры, положение) Внешний вид без изменения геометрии
Всегда ли вызывает repaint Да Нет
Примеры свойств width, height, margin, display, position color, background, opacity, box-shadow

Важно понимать: каждый reflow автоматически вызывает repaint, но не наоборот. Именно поэтому reflow считается более "тяжёлой" операцией с точки зрения производительности.

Современные браузеры оптимизируют эти процессы, накапливая изменения и выполняя их пакетно. Однако некоторые операции в JavaScript, например чтение свойств, связанных с геометрией элемента (offsetHeight, scrollTop и т.п.), могут вызвать принудительный reflow для получения актуальных данных.

Максим, ведущий фронтенд-разработчик: Работая над крупным e-commerce проектом, мы столкнулись с серьезными проблемами производительности при прокрутке страницы каталога товаров. Особенно заметны лаги были на мобильных устройствах. Профилируя страницу, обнаружили, что динамическое обновление счетчика товаров и фильтров при скролле вызывало каскадные reflow. Каждый раз, когда пользователь прокручивал страницу, скрипт считывал позицию элементов, что принудительно запускало reflow всего дерева документа. Решение было неочевидным: мы вынесли счетчик товаров и блок фильтров в отдельные слои с помощью will-change: transform, и перешли на requestAnimationFrame для обновления значений. Это позволило сократить время обработки кадра почти в 3 раза и полностью устранить дрожание страницы при прокрутке.

Пошаговый план для смены профессии

Диагностика проблем: как определить лишние Repaint и Reflow

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

7 проверенных способов оптимизации Repaint и Reflow

Зная, как работают процессы repaint и reflow, можно применить стратегии для минимизации их влияния на производительность приложения. Ниже представлены 7 проверенных способов оптимизации, позволяющих значительно повысить отзывчивость веб-интерфейсов. ⚡

1. Минимизация DOM-манипуляций

Каждое изменение DOM может потенциально вызвать reflow и repaint. Для оптимизации используйте следующие приемы:

  • Работайте с элементами, отключенными от DOM (display: none), перед их вставкой
  • Используйте DocumentFragment для накопления изменений
  • Избегайте множественных инкрементальных изменений стилей

Вместо последовательного добавления элементов:

JS
Скопировать код
// Неоптимально: вызовет reflow при каждом добавлении
for (let i = 0; i < 1000; i++) {
container.appendChild(document.createElement('div'));
}

Используйте фрагмент документа:

JS
Скопировать код
// Оптимально: только один reflow в конце
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
fragment.appendChild(document.createElement('div'));
}
container.appendChild(fragment);

2. Использование CSS-классов вместо инлайн-стилей

Изменение инлайн-стилей часто приводит к избыточным reflow. Вместо этого модифицируйте классы:

JS
Скопировать код
// Неоптимально: вызовет reflow при каждой модификации
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';

// Оптимально: один reflow после добавления класса
element.classList.add('new-dimensions');

3. Оптимизация анимаций с помощью transform и opacity

CSS-свойства transform и opacity могут быть аппаратно ускорены и обрабатываются на уровне композитора, минуя этапы layout и paint:

CSS
Скопировать код
/* Неоптимально: вызывает reflow */
@keyframes move-bad {
from { top: 0; left: 0; }
to { top: 200px; left: 200px; }
}

/* Оптимально: только композитинг без reflow */
@keyframes move-good {
from { transform: translate(0, 0); }
to { transform: translate(200px, 200px); }
}

4. Виртуализация больших списков данных

Для длинных списков рендеринг только видимой части (и небольшого буфера) может значительно сократить количество узлов в DOM:

JS
Скопировать код
// Пример использования виртуализации с библиотекой react-window
import { FixedSizeList } from 'react-window';

const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);

const Example = () => (
<FixedSizeList
height={400}
width={500}
itemCount={10000}
itemSize={35}
>
{Row}
</FixedSizeList>
);

5. Правильное использование свойства will-change

Свойство will-change информирует браузер о планируемых изменениях элемента, позволяя оптимизировать рендеринг:

CSS
Скопировать код
.moving-element {
will-change: transform;
}

.color-changing {
will-change: opacity, color;
}

Важно: Не злоупотребляйте will-change, так как это может привести к чрезмерному использованию памяти. Применяйте его только для элементов, которые действительно будут изменяться.

6. Асинхронное обновление DOM с помощью requestAnimationFrame

Синхронизация изменений DOM с циклом отрисовки браузера позволяет избежать промежуточных reflow:

JS
Скопировать код
// Вместо прямого изменения
function updatePositions() {
for (let i = 0; i < items.length; i++) {
items[i].style.left = items[i].offsetLeft + 10 + "px";
}
}

// Используем requestAnimationFrame
function updatePositionsOptimized() {
requestAnimationFrame(() => {
for (let i = 0; i < items.length; i++) {
items[i].style.left = items[i].offsetLeft + 10 + "px";
}
});
}

7. Отложенная загрузка и рендеринг вне экрана

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

JS
Скопировать код
// Пример Intersection Observer для ленивой загрузки
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
observer.unobserve(lazyImage);
}
});
});

document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));

Техника оптимизации Влияние на reflow Влияние на repaint Сложность внедрения
Минимизация DOM-манипуляций Высокое Высокое Средняя
CSS-классы вместо инлайн-стилей Высокое Среднее Низкая
Оптимизация с transform/opacity Очень высокое Очень высокое Низкая
Виртуализация данных Очень высокое Очень высокое Высокая
Использование will-change Среднее Высокое Низкая
requestAnimationFrame Высокое Высокое Средняя
Отложенная загрузка Высокое Высокое Средняя

Инструменты для профилирования и мониторинга производительности

Диагностика проблем с repaint и reflow требует специализированных инструментов, которые позволяют визуализировать и измерить эти процессы в реальном времени. Современные браузеры предоставляют мощные средства для анализа производительности рендеринга. 🛠️

Chrome DevTools Performance Panel

Наиболее мощный и распространенный инструмент для профилирования рендеринга:

  • Запись взаимодействия — нажмите Record и выполните действия на странице
  • Анализ Main thread — ищите красные блоки, указывающие на проблемы с производительностью
  • Выявление reflow — в записи они отмечены как "Layout" (оранжевый цвет)
  • Выявление repaint — обозначаются как "Paint" (зеленый цвет)

Для более глубокого анализа включите опцию "Enable advanced paint instrumentation" в настройках DevTools.

Lighthouse

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

  • Предоставляет оценки по категориям, включая Performance
  • Выявляет возможности для оптимизации рендеринга
  • Доступен прямо в Chrome DevTools или как CLI-инструмент

PerformanceObserver API

Позволяет программно отслеживать метрики производительности:

JS
Скопировать код
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long task detected:', entry.duration, 'ms');
}
});

observer.observe({entryTypes: ['longtask']});

FPS-метры и линейки

  • Chrome DevTools FPS meter — активируется через More Tools → Rendering → FPS meter
  • stats.js — легковесная JavaScript-библиотека для визуализации FPS в реальном времени

CSS Triggers

Справочный ресурс, показывающий, какие CSS-свойства вызывают layout, paint и composite:

  • Помогает выбирать CSS-свойства, минимально влияющие на производительность
  • Указывает различия в поведении между браузерами

Layer Panel в Chrome DevTools

Отображает композитные слои страницы:

  • Позволяет увидеть, какие элементы выделены в отдельные слои
  • Помогает определить, правильно ли работают ваши оптимизации с will-change
  • Активируется через More Tools → Layers

Использование этих инструментов в комплексе позволяет точно диагностировать проблемы с производительностью рендеринга и измерить эффект от применяемых оптимизаций. Регулярное профилирование должно стать неотъемлемой частью процесса разработки высокопроизводительных веб-приложений.

Практические кейсы: до и после оптимизации рендеринга

Теория без практики мертва. Рассмотрим реальные примеры оптимизации и их измеримые результаты, чтобы продемонстрировать эффективность описанных подходов. 📊

Кейс 1: Оптимизация бесконечной прокрутки контента

Проблема: Веб-приложение для просмотра новостей с бесконечной прокруткой демонстрировало падение FPS до 15-20 при быстром скролле, особенно на мобильных устройствах.

Первоначальная реализация:

JS
Скопировать код
window.addEventListener('scroll', function() {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight – 500) {
// Загрузка контента при приближении к концу страницы
for (let i = 0; i < 10; i++) {
const card = document.createElement('div');
card.className = 'news-card';
card.innerHTML = `
<img src="images/news-${currentItem++}.jpg">
<h3>Новость ${currentItem}</h3>
<p>Текст новости...</p>
`;
document.getElementById('news-container').appendChild(card);

// Запрос размеров для обновления макета
card.offsetHeight;
}
}
});

Проблемные места:

  • Прямое добавление элементов в DOM при скролле
  • Множественные reflow из-за последовательного appendChild
  • Принудительный reflow при чтении offsetHeight
  • Неоптимальная обработка событий скролла

Оптимизированная реализация:

JS
Скопировать код
// Троттлинг функции скролла
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}

// Оптимизированный обработчик скролла
const handleScroll = throttle(function() {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight – 500) {
requestAnimationFrame(loadMoreContent);
}
}, 100);

window.addEventListener('scroll', handleScroll);

function loadMoreContent() {
const fragment = document.createDocumentFragment();
const container = document.getElementById('news-container');

// Создаем все элементы вне DOM
for (let i = 0; i < 10; i++) {
const card = document.createElement('div');
card.className = 'news-card';
card.innerHTML = `
<img loading="lazy" data-src="images/news-${currentItem++}.jpg">
<h3>Новость ${currentItem}</h3>
<p>Текст новости...</p>
`;
fragment.appendChild(card);
}

// Добавляем все сразу, вызывая только один reflow
container.appendChild(fragment);

// Запускаем ленивую загрузку изображений
lazyLoadImages();
}

function lazyLoadImages() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
}
});
});

images.forEach(img => imageObserver.observe(img));
}

Результаты оптимизации:

  • FPS увеличился с 15-20 до стабильных 55-60 даже при быстром скролле
  • Количество reflow сократилось на 87%
  • Время загрузки и рендеринга новых карточек уменьшилось с 320мс до 78мс
  • Потребление памяти снизилось на 25% благодаря ленивой загрузке изображений

Анна, фронтенд-архитектор: Однажды нам предстояло оптимизировать дашборд с аналитикой, содержащий множество графиков и таблиц. Страница загружалась более 5 секунд, а переключение между вкладками вызывало зависание интерфейса на 1-2 секунды. Профилирование показало, что каждое обновление данных вызывало каскадные reflow и repaint по всей странице. Мы применили поэтапный подход к оптимизации. Сначала разделили интерфейс на независимые компоненты, изолировав их в отдельные композитные слои с помощью will-change: transform. Затем внедрили виртуализацию для таблиц с большим количеством строк — теперь рендерилось только видимое содержимое. Для графиков использовали Canvas вместо SVG там, где это было возможно. Самым неожиданным решением стало кэширование вычисленных размеров элементов. Мы обнаружили, что код часто запрашивал offsetWidth и другие метрики, что провоцировало reflow. Мы создали систему, которая сохраняла эти значения и обновляла их только при необходимости через ResizeObserver. Результат превзошёл ожидания: время загрузки сократилось до 1.8 секунды, а переключение между вкладками стало мгновенным. CPU-профиль показал снижение нагрузки на main thread на 78%. Клиент был в восторге, а мы получили ценный опыт, который теперь применяем во всех проектах.

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

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

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что такое repaint в веб-разработке?
1 / 5

Владимир Титов

редактор про сервисные сферы

Свежие материалы

Загрузка...