[Разработка веб-сайтов] Устройство современного веб-браузера Chrome (часть 4/4) (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
22-Ноя-2020 07:30

Это последний пост из серии 4-х постов, посвященной заглядыванию внутрь Chrome, и исследующей, как он обрабатывает наш код для отображения веб-сайта. В предыдущем посте мы рассмотрели *рендер-процесс (renderer process) и узнали о *композ-потоке (compositor thread). В этом посте мы рассмотрим, как *композ-поток обеспечивает плавное взаимодействие при вводе данных пользователем.

Часть 1
Часть 2
Часть 3
Часть 4 (текущая)

Об особенностях перевода

SPL
  • в ходе перевода, я старался вычленять из статьи ПОНЯТИЯ, т.е. текстовые единицы которые несут специальный (технический) смысл. В переводе эти понятия выделены по особенному — во первых понятия предваряются символом звёздочки, во-вторых в них вместо пробела используется тире. Например: *браузер-процесс, *сайто-изоляция. При переводе понятий, приоритет отдавался не красоте перевода, а желанию выделить, акцентировать, то что мы имеем дело с ПОНЯТИЕМ, а не с фигурой речи.
  • также, некоторые слова переведены неверно с точки зрения русского языка, в жаргонном стиле, например пайплайн, продакшен. У "технарей" такой перевод не вызовет затруднений, у остальных читателей прошу прощения.

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

Рисунок 1: Событие ввода проходит через *браузер-процесс в *рендер-процесс
*Композ-поток получает события ввода
В предыдущем посте мы рассмотрели, как *композ-поток может делать прокрутку плавной, создавая растровые слои. Если к странице не подключены слушатели входных событий, *композ-поток может создать новую кадр-композицию, полностью независимую от основной. Но что делать, если к странице прикреплены слушатели событий? Как *композ-поток узнает, нужно ли обрабатывать событие?

Рисунок 2: Viewport наводится на слои страницы
Понятие *нбс-региона
Поскольку выполнение JavaScript является задачей главного потока, при компоновке страницы *композ-поток отмечает область страницы, к которой прикреплены обработчики событий как "Non-Fast Scrollable Region" (*нбс-регион). Обладая этой информацией, *композ-поток может гарантировать, что входное событие будет отправлено в главный поток, если событие произойдет в этом регионе. Если входное событие приходит из-за пределов этого региона, то *композ-поток продолжает композицию нового кадра, не дожидаясь главного потока.

Рисунок 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});

Проверка является ли событие cancelable
Представьте, что на странице есть поле, ограничивающее направление прокрутки только горизонталью.
Использование опции passive: true в событии курсора означает, что прокрутка страницы может быть гладкой, но вертикальная прокрутка может начаться к тому времени, когда вы хотите выполнить preventDefault для ограничения направления прокрутки. Вы можете проверить это, используя метод event.cancelable.

Рисунок 5: Веб-страница с частью страницы у которой скролл только горизонтальный
document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // блокировка нативного скрола
        /*
        * сделать то что вам нужно
        */
    }
}, {passive: true});

Альтернативно, вы можете использовать CSS-правило, такое как действие касания, чтобы полностью устранить обработчик события.
#area {
  touch-action: pan-x;
}

Поиск цели события
Когда *композ-поток посылает входное событие в главный поток, первым делом выполняется проверка попадания в цель события. Проверка попадания использует *записи-отрисовки (paint records), которые были сгенерированы в процессе рендеринга, чтобы выяснить, что находится под координатами точки, в которой произошло событие.

Рисунок 6: Главный поток ищет *записи-отрисовки запрашивая что нарисовано в точке x.y
Минимизация распространения события в главный поток
В предыдущем посте мы обсуждали, что наш типичный дисплей обновляет экран 60 раз в секунду и что нам нужно идти в ногу с частотой кадров для плавной анимации. При вводе данных типичное устройство с сенсорным экраном передает события 60-120 раз в секунду, а типичная мышь передает события 100 раз в секунду. Событие ввода имеет более высокую точность, чем может обновить наш экран.
Если непрерывное событие, подобное сенсорному движению, посылается в основной поток 120 раз в секунду, то оно может вызвать избыточное количество проверок на попадание и выполнение JavaScript по сравнению с тем, как не быстро может обновляться экран.

Рисунок 7: События забивают временнУю ленту кадров что приводит к дрожанию страницы
Для минимизации излишних вызовов главного потока, Chrome объединяет (coalesces, *коалесирует) непрерывные события (такие как wheel, mousewheel, mousemove, pointermove, touchmove) и задерживает диспетчеризацию до момента, непосредственно предшествующего следующему requestAnimationFrame.

Рисунок 8: Та же лента но события *коалесируются и придерживаются
Любые дискретные события, такие как нажатие keydown, keyup, mouseup, mousedown, touchstart и touchchend отправляются немедленно.
Использование getCoalescedEvents для получения межкадровых событий
Для большинства веб-приложений, *коалесирование событий должно быть достаточным, чтобы обеспечить хороший пользовательский опыт. Однако, если вы строите такие вещи, как приложение для рисования и прокладываете путь на основе координат touchmove, вы можете потерять промежуточные координаты для отрисовки гладкой линии. В этом случае вы можете использовать метод getCoalescedEvents в событии курсора, чтобы получить информацию об этих *коалесированных событиях.

Рисунок 9: Плавный путь от жеста касания слева, рваный из-за *коалесирований пути справа
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        /* отрисовка линии используя координаты x и y */
    }
});

Следующие шаги
В этой серии постов мы рассмотрели внутреннюю работу веб-браузера. Если вы никогда не задумывались о том, почему DevTools рекомендует добавлять {passive: true} в обработчик событий или зачем писать атрибут async в теге сценария, я надеюсь, что эта серия прольёт некоторый свет на то, почему браузеру нужна эта информация для обеспечения более быстрой и плавной работы с веб.
= Использование Lighthouse
Если вы хотите, чтобы ваш код был удобен для браузера, но не знаете, с чего начать, то рекомендую Lighthouse — это инструмент, который проводит аудит любого сайта и даёт вам отчет о том, что делается правильно и что нуждается в улучшении. Читая список аудитов, вы также получаете представление о том, о чем беспокоится браузер.
= Изучаем как измерять производительность
Настройки производительности могут отличаться для разных сайтов, поэтому очень важно, чтобы вы измерили производительность вашего сайта и решили, что лучше всего подходит для него. Команда Chrome DevTools имеет несколько руководств по оценке производительности вашего сайта.
= Добавление Feature Policy на ваш сайт
Если вы хотите сделать дополнительный шаг, Feature Policy — это новая функция веб-платформы, которая может быть ограждением для вас, когда вы строите свой проект. Включение Feature Policy гарантирует определенное поведение вашего приложения и предотвращает ошибки. Например, если вы хотите гарантировать, что ваше приложение никогда не будет блокировать парсинг, вы можете запустить ваше приложение на основе политики синхронных сценариев. При включённом sync-script: 'none', блокирование JavaScript-ом парсера будет отключено. Это не позволит ни одному вашему коду заблокировать парасер, а браузеру будет не нужно беспокоиться о том, чтобы ставить парсер на паузу.
Итог
Когда я начинала создавать сайты, меня волновало только то, как я буду писать свой код и что поможет мне быть более продуктивной. Это важно, но мы также должны думать о том, как браузер принимает код, который мы пишем. Современные браузеры вкладывали и продолжают вкладывать средства в то, чтобы предоставить пользователям более качественный веб-интерфейс. Будьте любезны с браузером, организовывая наш код, и тем самым также, улучшая свой пользовательский опыт. Надеюсь, Вы присоединитесь ко мне в стремлении быть добрее к браузерам!

Огромное спасибо всем, кто рецензировал ранние проекты этой серии, включая (но не ограничиваясь): Alex Russell, Paul Irish, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani, Kinuko Yasuda, Nasko Oskov, и Charlie Reis.
Вам понравилась эта серия? Если у вас есть вопросы или предложения для будущих постов, я бы хотела услышать их от вас в разделе комментариев ниже или у меня в твиттере — @kosamari.
Часть 1
Часть 2
Часть 3
Часть 4 (текущая)
===========
Источник:
habr.com
===========

===========
Автор оригинала: Mariko Kosaka
===========
Похожие новости: Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_brauzery (браузеры), #_arhitektura_prilozhenij (архитектура приложений), #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 18:39
Часовой пояс: UTC + 5