Обработка событий прокрутки без ущерба для производительности

События прокрутки срабатывают десятки раз в секунду. Без правильной обработки они снизят производительность вашего сайта, разрядят батареи мобильных устройств и создадут рывки в пользовательском интерфейсе. Вот как оптимизировать обработчики прокрутки с помощью троттлинга, дебаунсинга и пассивных слушателей.
Ключевые выводы
- Троттлинг ограничивает выполнение функций фиксированными интервалами для стабильных обновлений
- Дебаунсинг ожидает окончания прокрутки перед выполнением ресурсоемких операций
- Пассивные слушатели обеспечивают немедленные оптимизации браузера
- Intersection Observer исключает события прокрутки для определения видимости
Проблема: почему обычные события прокрутки разрушают производительность
Каждое движение прокрутки вызывает множественные события — часто более 60 в секунду. Когда вы привязываете тяжелые вычисления к этим событиям, вы заставляете браузер:
- Постоянно блокировать главный поток
- Препятствовать плавной прокрутке
- Резко увеличивать использование CPU
- Разряжать батарею на мобильных устройствах
// НЕ ДЕЛАЙТЕ ТАК - срабатывает постоянно
window.addEventListener('scroll', () => {
calculateExpensiveAnimation();
updateNavigationState();
checkElementVisibility();
});
Решение 1: троттлинг для стабильных обновлений
Троттлинг ограничивает выполнение функции фиксированным интервалом. Идеально подходит для анимаций на основе прокрутки или индикаторов прогресса, которым нужны регулярные обновления.
function throttle(func, delay) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
return func.apply(this, args);
}
};
}
// Срабатывает максимум каждые 100мс
window.addEventListener('scroll', throttle(() => {
updateScrollProgress();
}, 100));
Когда использовать троттлинг:
- Индикаторы прогресса прокрутки
- Эффекты параллакса
- Обновления состояния навигации
- Отслеживание позиции в реальном времени
Решение 2: дебаунсинг для финальных значений
Дебаунсинг ожидает окончания прокрутки перед выполнением. Идеально подходит для ресурсоемких операций, которым нужна только финальная позиция прокрутки.
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
// Срабатывает через 200мс после окончания прокрутки
window.addEventListener('scroll', debounce(() => {
saveScrollPosition();
loadMoreContent();
}, 200));
Когда использовать дебаунсинг:
- Триггеры бесконечной прокрутки
- Отслеживание аналитики
- Сохранение позиции прокрутки
- Тяжелые вычисления DOM
Discover how at OpenReplay.com.
Решение 3: пассивные слушатели для мгновенной производительности
Пассивные слушатели сообщают браузеру, что вы не будете вызывать preventDefault()
, что позволяет немедленно оптимизировать прокрутку.
// Браузер может оптимизировать прокрутку немедленно
window.addEventListener('scroll', handleScroll, { passive: true });
Этот простой флаг улучшает производительность прокрутки, позволяя браузеру пропустить проверку того, будете ли вы предотвращать поведение по умолчанию. Мобильные браузеры особенно выигрывают от этой оптимизации.
Комбинирование техник для максимальной производительности
Для сложных взаимодействий с прокруткой комбинируйте несколько подходов:
// Троттлинг-обработчик с пассивным слушателем
const optimizedScroll = throttle(() => {
requestAnimationFrame(() => {
updateUI();
});
}, 16); // ~60fps
window.addEventListener('scroll', optimizedScroll, { passive: true });
Современная альтернатива: Intersection Observer
Для определения видимости полностью откажитесь от событий прокрутки:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
lazyLoadImage(entry.target);
}
});
});
observer.observe(document.querySelector('.lazy-image'));
Intersection Observer обрабатывает определение видимости без каких-либо слушателей прокрутки, обеспечивая превосходную производительность для ленивой загрузки и анимаций, запускаемых прокруткой.
Краткое руководство по выбору
Техника | Случай использования | Частота обновлений |
---|---|---|
Троттлинг | Индикаторы прогресса, параллакс | Фиксированные интервалы |
Дебаунсинг | Сохранение состояния, загрузка контента | После окончания прокрутки |
Пассивные | Любой обработчик прокрутки | Всегда (когда возможно) |
Intersection Observer | Определение видимости | Без событий прокрутки |
Советы по реализации
- Всегда используйте пассивные слушатели, если не нужен
preventDefault()
- Начинайте с задержки троттлинга 16мс для анимаций 60fps
- Используйте задержки дебаунсинга 200-300мс для действий, инициированных пользователем
- Рассмотрите Lodash для проверенных в бою реализаций
- Профилируйте с помощью Chrome DevTools для измерения фактических улучшений производительности
Заключение
Неоптимизированные обработчики прокрутки — убийцы производительности. Троттлинг обеспечивает контролируемые обновления для анимаций, дебаунсинг эффективно обрабатывает финальные значения, а пассивные слушатели предоставляют мгновенные оптимизации браузера. Для определения видимости полностью откажитесь от событий прокрутки в пользу Intersection Observer. Выберите правильную технику для вашего случая использования, и ваши пользователи отблагодарят вас временем работы батареи.
Часто задаваемые вопросы
Троттлинг выполняет вашу функцию через регулярные интервалы во время прокрутки, например, каждые 100мс. Дебаунсинг ожидает полного окончания прокрутки, затем выполняется один раз. Используйте троттлинг для непрерывных обновлений и дебаунсинг для финальных значений.
Нет, пассивные слушатели явно сообщают браузеру, что вы не будете вызывать preventDefault. Если вам нужно предотвратить поведение прокрутки по умолчанию, установите passive в false, но это влияет на производительность. Сначала рассмотрите альтернативные подходы.
Используйте Intersection Observer для любой логики, основанной на видимости, такой как ленивая загрузка, триггеры бесконечной прокрутки или анимации при прокрутке. Он более производителен, чем события прокрутки, и автоматически обрабатывает вычисления области просмотра без ручной проверки позиции.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.