Skip to content

Практическая работа №11: Оптимизация поиска, фильтрации и скролла


Цель работы: Углубить понимание механизмов работы браузера, изучить основные проблемы производительности веб-приложений и научиться применять продвинутые техники оптимизации для создания высокопроизводительных и отзывчивых пользовательских интерфейсов, а также проводить профессиональный анализ производительности с помощью Chrome DevTools.

Основные теоретические положения

Section titled “Основные теоретические положения”

1. Main Thread и Event Loop: Основы асинхронности в браузере

Section titled “1. Main Thread и Event Loop: Основы асинхронности в браузере”

Main Thread (основной поток) — это сердце любого веб-приложения. Он является единственным потоком, который отвечает за выполнение большинства критически важных задач в браузере: парсинг HTML и CSS, выполнение JavaScript-кода, обработка всех пользовательских событий (клики, ввод с клавиатуры, скролл), а также за отрисовку (layout, paint, composite) страницы. Поскольку JavaScript по своей природе является однопоточным языком, все эти операции выполняются последовательно. Если какая-либо задача в основном потоке занимает слишком много времени, она блокирует выполнение всех остальных задач, что приводит к “зависанию” интерфейса, отсутствию реакции на действия пользователя и общему ухудшению пользовательского опыта.

Event Loop (цикл событий) — это фундаментальный механизм, который позволяет JavaScript выполнять неблокирующие операции, несмотря на его однопоточную природу. Он постоянно мониторит две основные очереди: Task Queue (очередь задач) и Microtask Queue (очередь микрозадач). Когда основной поток завершает выполнение текущего кода (Call Stack пуст), Event Loop проверяет Microtask Queue, выполняет все микрозадачи, а затем переходит к Task Queue, забирая из нее следующую задачу и помещая ее в Call Stack для выполнения. Этот непрерывный цикл обеспечивает асинхронное выполнение операций, таких как обработка событий, сетевые запросы и таймеры, не блокируя основной поток.

2. Long Tasks и INP: Метрики интерактивности

Section titled “2. Long Tasks и INP: Метрики интерактивности”

Long Tasks (длительные задачи) — это любые задачи, которые блокируют основной поток браузера на 50 миллисекунд или дольше. Во время выполнения такой задачи браузер не может обрабатывать ввод пользователя, обновлять DOM или выполнять другие критически важные операции. Это приводит к заметным задержкам в реакции интерфейса, что воспринимается пользователем как “торможение” или “зависание” страницы. Длительные задачи часто возникают из-за сложных вычислений JavaScript, больших операций с DOM или синхронных сетевых запросов.

Interaction to Next Paint (INP) — это одна из ключевых метрик Core Web Vitals, которая измеряет общую отзывчивость страницы на действия пользователя. INP фиксирует задержку от момента начала взаимодействия пользователя (например, клика по кнопке, ввода текста) до момента, когда браузер визуально обновляет страницу в ответ на это взаимодействие. Высокий показатель INP (более 200 мс) указывает на плохую интерактивность и часто является прямым следствием наличия длительных задач, блокирующих основной поток. Оптимизация INP критически важна для обеспечения комфортного пользовательского опыта.

3. Debounce и Throttle: Управление частотой вызовов функций

Section titled “3. Debounce и Throttle: Управление частотой вызовов функций”

Для предотвращения чрезмерного количества вызовов функций, привязанных к часто срабатывающим событиям (например, input, scroll, resize), используются техники debounce и throttle.

Debounce (устранение дребезга) — это техника, которая откладывает выполнение функции до тех пор, пока не пройдет определенный период времени без ее повторного вызова. Если функция вызывается снова до истечения этого “таймаута”, таймер сбрасывается, и отсчет начинается заново. Функция будет выполнена только один раз после того, как серия вызовов прекратится. Debounce идеально подходит для событий, где важен только конечный результат после паузы в действиях пользователя, например, для поиска по мере ввода текста, автосохранения форм или валидации ввода.

Throttle (регулирование) — это техника, которая ограничивает частоту вызова функции до одного раза в заданный интервал времени. Если функция вызывается чаще, чем разрешено, последующие вызовы игнорируются или ставятся в очередь для выполнения после истечения интервала. Throttle подходит для событий, которые должны срабатывать регулярно, но не слишком часто, чтобы не перегружать основной поток. Примеры использования: обработка событий scroll (для ленивой загрузки или анимаций), resize (для адаптивной верстки) или обновление положения элементов при перетаскивании.

4. Repaint и Reflow: Оптимизация отрисовки

Section titled “4. Repaint и Reflow: Оптимизация отрисовки”

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

  • Reflow (перекомпоновка/перерасчет): Это процесс пересчета положения и размеров всех элементов на странице. Reflow запускается при любых изменениях, которые влияют на геометрию элементов или структуру DOM (например, изменение ширины, высоты, отступов, добавление/удаление элементов, изменение размера окна). Это одна из самых “дорогих” операций, так как она часто приводит к пересчету всей страницы или значительной ее части. Reflow всегда влечет за собой Repaint.
  • Repaint (перерисовка): Это процесс перерисовки пикселей на экране. Repaint происходит, когда изменяются только визуальные свойства элемента (например, цвет фона, видимость, box-shadow), но не его геометрия. Repaint менее затратен, чем Reflow, но все равно может влиять на производительность, особенно при частых изменениях или на больших областях экрана.

Минимизация Reflow и Repaint является ключевой стратегией оптимизации производительности фронтенда. Это достигается за счет группировки изменений DOM, использования CSS-свойств, которые не вызывают Reflow (например, transform, opacity), и избегания чтения стилей DOM между операциями записи.

5. requestAnimationFrame: Плавные анимации

Section titled “5. requestAnimationFrame: Плавные анимации”

requestAnimationFrame (rAF) — это специальный API браузера, предназначенный для планирования анимаций и визуальных обновлений. Он позволяет браузеру оптимизировать выполнение функции, вызывая ее непосредственно перед следующим циклом перерисовки. Это гарантирует, что:

  • Анимации будут синхронизированы с частотой обновления экрана (обычно 60 кадров в секунду), что обеспечивает максимальную плавность.
  • Браузер может приостанавливать анимации на неактивных вкладках, экономя ресурсы.
  • Избегаются “разрывы” кадров (tearing) и пропуски кадров, которые могут возникать при использовании setTimeout или setInterval для анимаций.

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

6. Виртуализация списков и Lazy Loading: Эффективная работа с большим контентом

Section titled “6. Виртуализация списков и Lazy Loading: Эффективная работа с большим контентом”
  • Виртуализация списков (List Virtualization): Это мощная техника оптимизации для рендеринга очень больших списков данных (тысячи или десятки тысяч элементов). Вместо того чтобы рендерить все элементы списка в DOM одновременно (что приводит к огромным затратам памяти и времени на Reflow/Repaint), виртуализация рендерит только те элементы, которые в данный момент видны пользователю во viewport, плюс небольшой буфер элементов сверху и снизу для плавного скролла. По мере скролла, невидимые элементы удаляются из DOM, а новые, входящие в viewport, добавляются. Это значительно уменьшает количество DOM-элементов, которые браузеру нужно обрабатывать, и кардинально улучшает производительность и отзывчивость.

  • Lazy Loading (отложенная загрузка): Этот подход заключается в отсрочке загрузки ресурсов (изображений, видео, скриптов, iframe) до тех пор, пока они действительно не понадобятся пользователю (например, когда они попадают в область видимости). Для изображений это означает, что атрибут src устанавливается только тогда, когда изображение становится видимым. Это значительно уменьшает начальную нагрузку на страницу, ускоряет ее отображение (First Contentful Paint, Largest Contentful Paint) и экономит трафик пользователя, так как не загружаются ресурсы, которые он, возможно, никогда не увидит. Современные браузеры поддерживают нативную ленивую загрузку изображений с помощью атрибута loading="lazy".

7. Web Workers: Вычисления вне основного потока

Section titled “7. Web Workers: Вычисления вне основного потока”

Web Workers предоставляют возможность выполнять JavaScript-скрипты в фоновом потоке, полностью отдельном от основного потока браузера. Это критически важно для выполнения ресурсоемких или длительных вычислений, которые в противном случае заблокировали бы основной поток и сделали бы интерфейс неотзывчивым. Web Workers не имеют прямого доступа к DOM, но могут обмениваться данными с основным потоком через механизм сообщений (postMessage, onmessage). Это позволяет переносить тяжелые операции (например, обработку больших объемов данных, сложные алгоритмы, парсинг файлов) в фоновый поток, сохраняя при этом плавность и отзывчивость пользовательского интерфейса.

Алгоритм выполнения работы

Section titled “Алгоритм выполнения работы”

Получение исходного кода проекта

Section titled “Получение исходного кода проекта”

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

  1. Клонирование с GitHub (рекомендуется):

    Terminal window
    git clone https://github.com/ghostITwe/optimization-practice
    cd optimization-practice
  2. Скачивание архива: Если вы не используете Git, вы можете скачать проект в виде ZIP-архива по прямой ссылке. Распакуйте архив в удобное для вас место.

После получения проекта, все дальнейшие шаги выполняются в директории optimization-practice

1. Изучение и анализ неоптимизированного проекта

Section titled “1. Изучение и анализ неоптимизированного проекта”
  1. Откройте файл index.html (находящийся в корневой папке проекта) в браузере. Это неоптимизированная версия галереи изображений.
  2. Взаимодействие: Попробуйте активно вводить текст в поле поиска и быстро скроллить страницу. Обратите внимание на:
    • Заметные задержки в реакции интерфейса.
    • Низкий показатель FPS (Frames Per Second) в реальном времени (монитор в правом нижнем углу).
    • Общее ощущение “торможения” или “неотзывчивости” страницы.
  3. Анализ с DevTools:
    • Откройте Chrome DevTools (F12) и перейдите на вкладку “Performance”.
    • Запустите запись профиля производительности (кнопка с кружком) во время активного взаимодействия с галереей (поиск, скролл).
    • Остановите запись и внимательно изучите полученный профиль. Обратите внимание на:
      • Наличие и длительность “Long Tasks” (красные треугольники в верхней части графика).
      • Активность в “Main” потоке (особенно в секциях “Scripting” и “Rendering”).
      • Показатели FPS (зеленый график в верхней части).
    • Сделайте скриншоты наиболее показательных моментов профиля для последующего сравнения.

2. Оптимизация поиска с debounce

Section titled “2. Оптимизация поиска с debounce”
  1. Откройте файл index.html в текстовом редакторе.
  2. Найдите секцию, отвечающую за обработку поиска (ориентируйтесь на комментарии // UNOPTIMIZED SEARCH & FILTER).
  3. Реализуйте debounce: Создайте функцию debounce(func, delay) (см. теоретический материал или примеры в интернете) и примените её к функции, которая выполняет фильтрацию элементов галереи по поисковому запросу. Установите задержку delay в 300 миллисекунд.
  4. Проверка: Сохраните изменения и обновите страницу в браузере. Повторите действия из Шага 1 (активный ввод текста). Убедитесь, что:
    • Поиск теперь срабатывает только после небольшой паузы в вводе.
    • Интерфейс стал более отзывчивым во время ввода.
    • Проведите повторный анализ в DevTools и сравните с предыдущим профилем. Отметьте уменьшение количества вызовов функции поиска и улучшение плавности.

3. Оптимизация скролла с throttle

Section titled “3. Оптимизация скролла с throttle”
  1. В файле index.html найдите секцию, отвечающую за обработку скролла (ориентируйтесь на комментарии // SCROLL OPTIMIZATION PROBLEM).
  2. Реализуйте throttle: Создайте функцию throttle(func, delay) (см. теоретический материал или примеры в интернете) и примените её к обработчику события scroll окна. Установите интервал delay в 100 миллисекунд.
  3. Проверка: Сохраните изменения и обновите страницу. Повторите действия из Шага 1 (активный скролл). Убедитесь, что:
    • Скролл стал более плавным, без заметных “рывков”.
    • Проведите повторный анализ в DevTools и сравните с предыдущим профилем. Отметьте уменьшение частоты вызовов обработчика скролла и улучшение FPS.

4. Кэширование DOM-элементов и финальный анализ

Section titled “4. Кэширование DOM-элементов и финальный анализ”
  1. Кэширование DOM-элементов: Модифицируйте функцию фильтрации таким образом, чтобы она не искала элементы галереи в DOM при каждом вызове. Вместо этого, при первоначальной загрузке страницы, получите все элементы галереи и сохраните их ссылки в JavaScript-массиве. Затем используйте этот массив для доступа к элементам при фильтрации.
  2. Удаление искусственных задержек: Убедитесь, что из кода удалены все искусственные задержки (например, циклы с Math.sqrt), которые были добавлены для демонстрации проблем производительности.
  3. Финальный анализ с DevTools:
    • Сравните производительность вашей полностью оптимизированной версии с исходной неоптимизированной версией (файл index.html).
    • Проведите финальную запись профиля производительности в DevTools для оптимизированной версии.
    • Подготовьте отчет, включающий:
      • Скриншоты профилей производительности “до” и “после” оптимизации.
      • Подробные выводы о том, как каждая примененная техника повлияла на метрики производительности (Long Tasks, FPS, Scripting Time).
      • Объяснение, почему выбранные методы оптимизации оказались эффективными.
  1. Подробно объясните роль Main Thread и Event Loop в работе браузера. Как они взаимодействуют и почему понимание этого взаимодействия критически важно для оптимизации?
  2. Проанализируйте различия между debounce и throttle. Приведите не менее трех реальных сценариев использования для каждой техники, объясняя, почему именно она является предпочтительной в каждом случае.
  3. Что такое Long Task и как она напрямую влияет на метрику INP? Какие типы операций чаще всего приводят к возникновению Long Tasks?
  4. Опишите механизм работы requestAnimationFrame. В чем его принципиальное преимущество перед setTimeout или setInterval для реализации плавных визуальных обновлений и анимаций?
  5. Объясните концепцию Web Workers. В каких случаях их применение оправдано, и какие ограничения они имеют?
  6. Какие основные метрики и разделы в Chrome DevTools Performance следует анализировать для выявления проблем с производительностью основного потока? Как интерпретировать полученные данные?
  7. Почему кэширование DOM-элементов является важной техникой оптимизации, особенно при частых операциях с ними (например, при фильтрации больших списков)?
  8. Как lazy loading изображений и виртуализация списков способствуют улучшению производительности страницы с большим количеством медиа и данных? Объясните механизмы их работы.
  • Изучение и анализ неоптимизированной версии (20%): Корректное выявление проблем производительности с помощью DevTools, предоставление скриншотов и первичных выводов.
  • Реализация debounce для поиска (25%): Правильная реализация функции debounce и её корректное применение к поиску, демонстрация улучшения интерактивности.
  • Реализация throttle для скролла (25%): Правильная реализация функции throttle и её корректное применение к обработчику скролла, демонстрация улучшения плавности.
  • Кэширование DOM-элементов и финальный анализ (20%): Модификация кода для использования кэшированных DOM-элементов, удаление искусственных задержек, проведение финального анализа производительности с DevTools и предоставление отчета с выводами и скриншотами.
  • Ответы на контрольные вопросы (10%): Полные и развернутые ответы на все контрольные вопросы, демонстрирующие глубокое понимание теоретического материала.