Используете ли вы стриминг?

Streams (потоки) предоставляют интерфейс для асинхронной записи и чтения данных. Данные читаются небольшими чанками (chunks) и только часть данным может присутствовать в памяти в определенный момент времени. 

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

Можно создать один поток для нескольких ресурсов. Например, используя сервис воркер (service worker) вы можете создать поток где часть страницы получена из локального кеша, а часть по сети. Как отмечает Джеф Посник (Jeff Posnick), если ваше приложение использует CMS, которая собирает серверный HTML из нескольких шаблонов, вы можете стримить ответ, повторив всю логику шаблонизации на серсис-воркере. О том как это сделать можно прочитать в статье Джейка Арчибальда (Jake Archibald) The Year of Web Streams. Вы можете добиться значительного выигрыша в производительности.

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

Сейчас стриминг частично поддерживается в Chrome, Firefox, Safari и Edge. Также вы можете использовать экспериментальную поддержку стриминга запросов, которая позволит вам отправить запрос в процессе генерации тела запроса. Эта поддержка доступна в Chrome 85.

Подумайте о connection-aware и device memory-aware

Данные могут стоить дорого. По мере роста сайта, необходимо помнить о тех пользователях, которые решили их экономить. Save-Data client hint request header позволяет нам настроить приложение для таких пользователей.

Можно заменять изображения с высоким DPI на изображения с более низким DPI , удалить веб-шрифты, эффекты параллакса, превью, бесконечную прокрутку. Отключить автовоспроизведение видео, server push, уменьшить количество отображаемых элементов и понизить качество изображения, или даже изменить способ загрузки HTML. Тим Верееке (Tim Vereecke) опубликовал очень подробную статью о стратегиях data-s (h)aver.

Кто же использует save-data?

У 18% всех пользователей Chrome на Android включен Lite Mode, возможно это значение даже выше. Согласно исследованию Саймона Хёрна (Simon Hearne) это отношение выше для дешевых устройств, но есть выбросы.

При включенном режиме Save-Data Chrome Mobile будет проксировать запросы с deffered scripts, принудительно установит font-display: swap и включит ленивую загрузку. Лучше управлять этими оптимизациями самостоятельно, а не полагаться на браузер.

В настоящее время этот хэдер поддерживается только в Chromium: в Android-версии или через расширение Data Sever на десктопах. Кроме этого можно использовать Network Information API для выбора режима работы с тяжёлыми модулями JavaScript, изображениями высокого разрешения и видео на основе информации о типе сетевого соединения. Network Information API и, в частности, navigator.connection.effectiveType используют значения RTT, downlink, effectiveType (и некоторые другие) чтобы предоставить нам информацию о соединении и данных.

В этом контексте Макс Бёк (Max Böck) рассказывает о connection-aware components, а Адди Османи — об adaptive module serving. Например, мы можем написать React-компонент, который будет по-разному отображаться для разных типов соединений.

В статье Макса рассматривается компонент <Media /> которые может показывать:
  • Offline: изображение с alt,
  • 2G / save-data mode: изображение с низким разрешением,
  • 3G on non-Retina screen: изображение со средним разрешением,
  • 3G on Retina screens: изображение с высоким разрешением, 4G: HD-видео.
Дин Хьюм (Dean Hume) предлагает реализацию подобной логики на service worker. Для видео мы могли бы по умолчанию отображать видеопостер, а при лучшем соединении иконку «Play» и видеоплеер. В качестве фоллбека можно использовать EventListener canplaythrough и Promise. race () чтобы остановить загрузку, если событие canplaythrough не сработает в течение 2 секунд.

Если вы хотите погрузиться в эту тему немного глубже, Виталий Фридман предлагает для изучить эти источники:
Кроме этого можно динамически настраивать работу с данными в зависимости от доступной памяти устройства с помощью Device Memory API. navigator.deviceMemory возвращает значение RAM в гигабайтах, округленное до ближайшей степени двойки. В API также есть Client Hints Header, Device-Memory, который сообщает то же значение.

Service workers для кэширования и фоллбека

Самая лучшая сетевая оптимизация - вообще не использовать сеть, а раздавать данные прямо из локального кеша (хотя есть исключения). Если вы используете HTTPS, вы можете кэшировать статику в service worker cache и предоставлять офлайновые fallback’и из этого кеша

Фил Уолтон предлагает с помощью service workers отправлять меньше HTML, программно генерируя ответы от сервера. Service worker может запросить у сервера только необходимый минимум данных а затем программно преобразовать эти данные в полный HTML-документ. После посещения сайта будет установлен service worker, и браузер больше никогда не будет запрашивать полную HTML-страницу. Это может очень положительно сказаться на производительности.

А что насчёт поддержки браузеров? Service worker’ы широко поддерживаются, а если даже нет, то произойдет фоллбека на обычный сетевой сценарий. Использование сервис-воркеров позволяет значительно улучшить производительность. Еще большего выигрыша можно добиться используя функцию Background Fetch, которая также позволяет выполнять фоновую загрузку  через service worker.

Существует множество вариантов использования service worker. Например, функция "Сохранить для офлайна", обработать битые изображения, внедрить обмен сообщениями между вкладками или выбрать стратегии кэширования на основе типов запросов. В целом, самая надежная стратегия — хранить в кеше ядро приложения и несколько основных страниц.

Есть несколько моментов о которых следует помнить. Остерегайтесь range requests в Safari. Если вы когда-либо натыкались на ошибку DOMException: Quota exceeded в консоли браузера, то загляните вот в статью Херардо Родригеса (Gerardo Rodriguez). Когда 7КБ превращаются в 7МБ. В своей статье Херардо Gerardo рекомненудет убедиться что вы используете правильные CORS-хедеры и не кешируете opaque ответы.

Чтобы погрузиться в тему работы с service worker’ами, Виталий Фридман рекомендует обратиться к этим источникам: