Ввод поступает в композитор
Это последняя из четырех частей серии блогов, посвященных Chrome; исследуя, как он обрабатывает наш код для отображения веб-сайта. В предыдущем посте мы рассмотрели процесс рендеринга и узнали о композиторе . В этом посте мы рассмотрим, как композитор обеспечивает плавное взаимодействие при вводе пользователем данных.
Входные события с точки зрения браузера
Когда вы слышите «события ввода», вы можете думать только о вводе текста в текстовое поле или щелчке мыши, но с точки зрения браузера ввод означает любой жест пользователя. Прокрутка колеса мыши — это событие ввода, а прикосновение или наведение курсора мыши — также событие ввода.
Когда происходит жест пользователя, например прикосновение к экрану, процесс браузера первым получает этот жест. Однако процесс браузера знает только о том, где произошел этот жест, поскольку содержимое внутри вкладки обрабатывается процессом рендеринга. Таким образом, процесс браузера отправляет тип события (например, touchstart
) и его координаты процессу рендеринга. Процесс рендеринга обрабатывает событие соответствующим образом, находя цель события и запуская подключенные прослушиватели событий.
Композитор получает входные события
В предыдущем посте мы рассмотрели, как наборщик может плавно обрабатывать прокрутку путем компоновки растеризованных слоев. Если к странице не подключены прослушиватели входных событий, поток Compositor может создать новый составной фрейм, полностью независимый от основного потока. Но что, если к странице были прикреплены некоторые прослушиватели событий? Как поток компоновщика узнает, нужно ли обрабатывать событие?
Понимание области с небыстрой прокруткой
Поскольку запуск JavaScript является задачей основного потока, при компоновке страницы поток компоновщика помечает область страницы, к которой прикреплены обработчики событий, как «Область с небыстрой прокруткой». Имея эту информацию, поток наборщика может гарантировать отправку входного события в основной поток, если событие происходит в этом регионе. Если входное событие поступает из-за пределов этого региона, поток набора продолжает компоновку нового кадра, не дожидаясь основного потока.
Будьте внимательны при написании обработчиков событий.
Распространенным шаблоном обработки событий в веб-разработке является делегирование событий. Поскольку события пузырьковые, вы можете прикрепить один обработчик событий к самому верхнему элементу и делегировать задачи на основе цели события. Возможно, вы видели или писали код, подобный приведенному ниже.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Поскольку вам нужно написать только один обработчик событий для всех элементов, эргономика этого шаблона делегирования событий привлекательна. Однако, если вы посмотрите на этот код с точки зрения браузера, теперь вся страница помечена как область с небыстрой прокруткой. Это означает, что даже если ваше приложение не заботится о вводе из определенных частей страницы, поток наборщика должен взаимодействовать с основным потоком и ждать его каждый раз, когда поступает событие ввода. Таким образом, возможность плавной прокрутки наборщика терпит поражение.
Чтобы избежать этого, вы можете передать параметры passive: true
в прослушивателе событий. Это намекает браузеру, что вы все еще хотите прослушивать событие в основном потоке, но наборщик также может продолжить и составить новый кадр.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Проверьте, можно ли отменить мероприятие
Представьте, что у вас есть поле на странице, и вы хотите ограничить направление прокрутки только горизонтальной прокруткой.
Использование опции passive: true
в событии указателя означает, что прокрутка страницы может быть плавной, но вертикальная прокрутка могла начаться к тому времени, когда вы хотите preventDefault
, чтобы ограничить направление прокрутки. Вы можете проверить это, используя метод event.cancelable
.
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
Альтернативно вы можете использовать правило CSS, такое как touch-action
, чтобы полностью исключить обработчик событий.
#area {
touch-action: pan-x;
}
Поиск цели события
Когда поток наборщика отправляет входное событие в основной поток, первое, что необходимо выполнить, — это проверка попадания, чтобы найти цель события. В тесте на попадание используются данные записей рисования, созданные в процессе рендеринга, чтобы выяснить, что находится под координатами точки, в которой произошло событие.
Минимизация отправки событий в основной поток
В предыдущем посте мы обсуждали, как наш типичный дисплей обновляет экран 60 раз в секунду и как нам нужно поддерживать эту частоту для плавной анимации. При вводе типичное устройство с сенсорным экраном передает событие касания 60–120 раз в секунду, а типичная мышь — 100 раз в секунду. Событие ввода имеет более высокую точность, чем может обновить наш экран.
Если непрерывное событие, такое как touchmove
отправлялось в основной поток 120 раз в секунду, это может вызвать чрезмерное количество проверок нажатия и выполнения JavaScript по сравнению с тем, насколько медленно может обновляться экран.
Чтобы свести к минимуму чрезмерные вызовы основного потока, Chrome объединяет непрерывные события (такие как wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) и откладывает отправку до следующего requestAnimationFrame
.
Любые дискретные события, такие как keydown
, keyup
, mouseup
, mousedown
, touchstart
и touchend
отправляются немедленно.
Используйте getCoalescedEvents
для получения внутрикадровых событий.
Для большинства веб-приложений объединенных событий должно быть достаточно, чтобы обеспечить хорошее взаимодействие с пользователем. Однако если вы создаете такие вещи, как приложение для рисования, и прокладываете путь на основе координат touchmove
, вы можете потерять промежуточные координаты, чтобы нарисовать плавную линию. В этом случае вы можете использовать метод getCoalescedEvents
в событии указателя, чтобы получить информацию об этих объединенных событиях.
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
Следующие шаги
В этой серии мы рассмотрели внутреннюю работу веб-браузера. Если вы никогда не задумывались о том, почему DevTools рекомендует добавлять {passive: true}
в обработчик событий или почему вы можете написать атрибут async
в теге скрипта, я надеюсь, что эта серия прольет свет на то, почему браузеру нужна эта информация для более быстрого и плавного предоставления информации. веб-опыт.
Использовать маяк
Если вы хотите, чтобы ваш код хорошо отображался в браузере, но не знаете, с чего начать, Lighthouse — это инструмент, который проводит аудит любого веб-сайта и предоставляет вам отчет о том, что сделано правильно, а что нуждается в улучшении. Чтение списка проверок также дает вам представление о том, какие вещи заботят браузер.
Узнайте, как измерять производительность
Настройки производительности могут различаться для разных сайтов, поэтому крайне важно измерить производительность вашего сайта и решить, что лучше всего подходит для вашего сайта. У команды Chrome DevTools есть несколько руководств по измерению производительности вашего сайта .
Добавьте политику функций на свой сайт
Если вы хотите сделать дополнительный шаг, Feature Policy — это новая функция веб-платформы, которая может стать для вас ограждением при создании проекта. Включение политики функций гарантирует определенное поведение вашего приложения и предотвращает ошибки. Например, если вы хотите быть уверенным, что ваше приложение никогда не будет блокировать синтаксический анализ, вы можете запустить свое приложение в соответствии с политикой синхронных сценариев. Если включен sync-script: 'none'
, выполнение блокирующего синтаксический анализатор JavaScript будет запрещено. Это предотвращает блокировку синтаксического анализатора каким-либо вашим кодом, и браузеру не нужно беспокоиться о приостановке синтаксического анализатора.
Заворачивать
Когда я начал создавать веб-сайты, меня почти заботило только то, как я буду писать свой код и что поможет мне быть более продуктивным. Эти вещи важны, но нам также следует подумать о том, как браузер воспринимает код, который мы пишем. Современные браузеры вкладывали и продолжают инвестировать в способы обеспечения лучшего взаимодействия с пользователями в Интернете. Хорошее отношение к браузеру за счет организации нашего кода, в свою очередь, улучшает ваш пользовательский опыт. Надеюсь, вы присоединитесь ко мне в стремлении быть вежливыми с браузерами!
Огромное спасибо всем, кто рецензировал ранние черновики этой серии, включая (но не ограничиваясь): Алексом Расселом , Полом Айришем , Меггин Кирни , Эриком Бидельманом , Матиасом Байненсом , Адди Османи , Кинуко Ясуда , Наско Осков и Чарли Рейс.
Вам понравился этот сериал? Если у вас есть какие-либо вопросы или предложения по поводу будущего поста, я буду рад услышать ваше мнение в разделе комментариев ниже или на @kosamari в Твиттере.