Обработчики ввода потенциально являются источниками проблем с производительностью в приложениях, поскольку они могут заблокировать завершение кадров, а также вызывать дополнительную (и совершенно ненужную) работу по пересчету макета
TL;DR
- "Не используйте обработчики ввода с длительным рабочим циклом_– они могут заблокировать прокрутку."
- Не изменяйте стили в обработчиках ввода.
- Оптимизируйте обработчики; запоминайте значения событий и реализуйте изменения стилей при следующем обратном вызове функции requestAnimationFrame.
Не используйте обработчики ввода с длительным рабочим циклом
В самом быстром варианте, когда пользователь вводит данные на страницу, поток компоновки страницы может принять касание пользователя и просто переместить контент. Основной поток, в котором выполняется код JavaScript, перерисовка, рассчитывается макет и применяются стили, при этом задействован не будет вообще.
Однако, если прикрепить обработчик ввода, например touchstart
, touchmove
или touchend
, поток компоновщика должен будет подождать завершения выполнения рабочего цикла этого обработчика, поскольку может быть вызвана функция preventDefault()
и прокрутка будет остановлена. Даже если функция preventDefault()
не вызывается, компоновщик должен ждать, а выполняемая пользователем прокрутка будет заблокирована, что может привести к подвисанию или пропуску кадров.
Короче говоря, рабочий цикл любых используемых обработчиков ввода должен выполняться быстро, чтобы компоновщик мог делать свою работу.
Не изменяйте стили в обработчиках ввода
Рабочий цикл обработчиков ввода, например прокрутки и касаний, выполняется перед тем, как будут выполняться обратные вызовы функции requestAnimationFrame
.
Если внутри одного из таких обработчиков вносится визуальное изменение, то в начале выполнения обратного вызова requestAnimationFrame
необходимо будет применить изменение стиля. Если после этого выполнить чтение визуальных свойств в начале обратного вызова requestAnimationFrame, как рекомендуется в статье "Не используйте большие, сложные макеты и избегайте подтормаживания макетов", вы вызовите принудительный синхронный перерасчет макета!
Оптимизируйте обработчики прокрутки
Обе описанные выше неполадки имеют одно решение: следует всегда откладывать внесение визуальных изменений до следующего обратного вызова requestAnimationFrame
:
function onScroll (evt) {
// Store the scroll value for laterz.
lastScrollY = window.scrollY;
// Prevent multiple rAF callbacks.
if (scheduledAnimationFrame)
return;
scheduledAnimationFrame = true;
requestAnimationFrame(readAndUpdatePage);
}
window.addEventListener('scroll', onScroll);
Кроме того, обработчики ввода при этом останутся необременительными, а это отлично, поскольку теперь прокрутка или касания при выполнении кода с большим объемом вычислений не будут блокироваться!