Взгляд изнутри на современный веб-браузер (часть 4)

Mariko Kosaka

Ввод поступает в композитор

Это последняя из четырех частей серии блогов, посвященных Chrome; исследуя, как он обрабатывает наш код для отображения веб-сайта. В предыдущем посте мы рассмотрели процесс рендеринга и узнали о композиторе . В этом посте мы рассмотрим, как композитор обеспечивает плавное взаимодействие при вводе пользователем данных.

Входные события с точки зрения браузера

Когда вы слышите «события ввода», вы можете думать только о вводе текста в текстовое поле или щелчке мыши, но с точки зрения браузера ввод означает любой жест пользователя. Прокрутка колеса мыши — это событие ввода, а прикосновение или наведение курсора мыши — также событие ввода.

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

входное событие
Рис. 1. Входное событие, маршрутизируемое через процесс браузера в процесс рендеринга.

Композитор получает входные события

Рисунок 2. Область просмотра, нависшая над слоями страницы.

В предыдущем посте мы рассмотрели, как наборщик может плавно обрабатывать прокрутку путем компоновки растеризованных слоев. Если к странице не подключены прослушиватели входных событий, поток Compositor может создать новый составной фрейм, полностью независимый от основного потока. Но что, если к странице были прикреплены некоторые прослушиватели событий? Как поток компоновщика узнает, нужно ли обрабатывать событие?

Понимание области с небыстрой прокруткой

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

ограниченная область без быстрой прокрутки
Рисунок 3: Схема описанного ввода в область с небыстрой прокруткой.

Будьте внимательны при написании обработчиков событий.

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

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

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

Полностраничная область без быстрой прокрутки
Рис. 4. Схема описанного ввода в область с небыстрой прокруткой, охватывающую всю страницу.

Чтобы избежать этого, вы можете передать параметры passive: true в прослушивателе событий. Это намекает браузеру, что вы все еще хотите прослушивать событие в основном потоке, но наборщик также может продолжить и составить новый кадр.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

Проверьте, можно ли отменить мероприятие

прокрутка страницы
Рисунок 5. Веб-страница, часть страницы которой привязана к горизонтальной прокрутке.

Представьте, что у вас есть поле на странице, и вы хотите ограничить направление прокрутки только горизонтальной прокруткой.

Использование параметра 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;
}

Поиск цели события

тест на попадание
Рис. 6. Основной поток, просматривающий записи рисования, спрашивает, что нарисовано в точке xy.

Когда поток наборщика отправляет входное событие в основной поток, первое, что необходимо выполнить, — это проверка попадания, чтобы найти цель события. Тест на попадание использует данные записей рисования, сгенерированные в процессе рендеринга, чтобы выяснить, что находится под координатами точки, в которой произошло событие.

Минимизация отправки событий в основной поток

В предыдущем посте мы обсуждали, как наш типичный дисплей обновляет экран 60 раз в секунду и как нам нужно поддерживать эту частоту для плавной анимации. При вводе типичное устройство с сенсорным экраном передает событие касания 60–120 раз в секунду, а типичная мышь — 100 раз в секунду. Событие ввода имеет более высокую точность, чем может обновить наш экран.

Если непрерывное событие, такое как touchmove отправлялось в основной поток 120 раз в секунду, это может вызвать чрезмерное количество проверок нажатия и выполнения JavaScript по сравнению с тем, насколько медленно может обновляться экран.

нефильтрованные события
Рисунок 7. События, заполняющие временную шкалу кадра, вызывающие зависание страницы.

Чтобы свести к минимуму чрезмерные вызовы основного потока, Chrome объединяет непрерывные события (такие как wheel , mousewheel , mousemove , pointermove , touchmove ) и откладывает отправку до следующего requestAnimationFrame .

объединенные события
Рисунок 8. Тот же график, что и раньше, но события объединяются и откладываются.

Любые дискретные события, такие как keydown , keyup , mouseup , mousedown , touchstart и touchend , отправляются немедленно.

Используйте getCoalescedEvents для получения внутрикадровых событий.

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

getCoalescedEvents
Рис. 9. Плавный путь сенсорного жеста слева, объединенный ограниченный путь справа.
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 в Твиттере.