Предлагаем вашему вниманию подборку всевозможных лайфхаков и трюков для оптимизации количества загружаемого кода и файлов, а также общего ускорения загрузки веб-страниц.
Статья основана на стенограмме выступления Виталия Фридмана из Smashing Magazine на декабрьской конференции Holy JS 2017 Москва.
Чтобы нам с вами не было скучно, я решил представить эту историю в формате небольшой игры, назвав ее «Отзывчивые приключения».
Уровней в игре будет пять, и начнём мы с простого уровня — сжатия.
Уровень 1 – Сжатие
Сжатие есть сжатие, и во внешнем интерфейсе можно сжимать, например, изображения, текст, шрифты и так далее.
Если есть необходимость максимально оптимизировать страницу в плане текста, то на практике обычно используют библиотеку сжатия данных gzip. Наиболее часто используемой реализацией gzip является zlib, в которой используется комбинация алгоритмов кодирования LZ77 и Хаффмана.
Обычно нас интересует, насколько библиотека должна сжимать, потому что чем лучше она это делает, тем дольше длится процесс.
Обычно мы выбираем либо быстрое сжатие, либо хорошее сжатие, так как невозможно добиться одновременно быстрого и хорошего сжатия.
Но нас, как разработчиков, волнуют два аспекта: размер файла и скорость сжатия/распаковки – для статического и динамического веб-контента.
Существуют алгоритмы сжатия данных Brotli и Zopfli. Zopfli можно считать более эффективной, но более медленной версией gzip. Brotli — это новый формат сжатия и распаковки без потерь.
В будущем мы сможем использовать Brotli. Но сейчас не все браузеры его поддерживают, и нам нужна полноценная поддержка.
Бротли и Цопфли
- Brotli значительно медленнее сжимает данные по сравнению с gzip, но обеспечивает гораздо лучшее сжатие.
- Brotli — это формат сжатия без потерь с открытым исходным кодом.
- Распаковка Бротли быстрая - сравнима с zlib.
- Brotli дает преимущество при работе с большими файлами на медленных соединениях.
- Brotli сжимает на 14-39% эффективнее.
- Идеально подходит для HTML, CSS, JavaScript, SVG и всего текстового.
- Поддержка Brotli ограничена соединениями HTTPS.
- Zopfli часто используется для оперативного сжатия, но является хорошей альтернативой для однократного сжатия статического контента.
Стратегия сжатия Brotli/Zopfli
Стратегия выглядит следующим образом:- Предварительно сжимайте статические ресурсы с помощью Brotli + Gzip.
- Сжимайте на лету с помощью Brotli HTML с уровнем сжатия 1–4.
- Проверьте поддержку Brotli в CDN (KeyCDN, CDN77, Fastly).
- Используйте Zopfli, если невозможно установить/поддержать Brotli на сервере.
Уровень 2 — Изображения
Но что мы будем делать с изображениями? Предположим, у вас есть хорошая целевая страница со шрифтами и изображениями.
Страница должна загружаться очень быстро.
И мы говорим о экстремальном уровне оптимизации изображения.
Это проблема, и она не надуманная.
Мы предпочитаем об этом не говорить, поскольку в отличие от JS изображения не блокируют рендеринг страницы.
На самом деле это большая проблема, поскольку размер изображений со временем увеличивается.
Сейчас уже используются экраны 4К, скоро появится и 8К.
В целом 90% пользователей видят на странице 5,4 МБ изображений — это очень много.
«та проблема, которая требует решения.
Конкретизируем проблему.
Что делать, если у вас есть большое изображение с прозрачной тенью, как в примере ниже.
Как его сжать? Ведь png достаточно тяжелый, и после сжатия тень будет выглядеть не очень хорошо.
Какой формат мне выбрать? JPEG? Тень тоже будет выглядеть не очень хорошо.
Что может быть сделано?
Один из лучших вариантов — разделить изображения на две части.
Поместите основу изображения в формате JPEG, а тень в формате PNG. Далее соедините два изображения в формате svg.
Почему это хорошо? Потому что образ, который весил 1,5 МБ, теперь весит 270 КБ.
Это большая разница.
Но есть еще пара хитростей.
Вот один из них.
Возьмем два изображения, которые визуально отображаются на сайте, с одинаковыми пропорциями.
Первый очень плохого качества, имеет реальный и визуальный размер 600 х 400 пикселей, а ниже такой же, но визуально уменьшенный до 300 х 200 пикселей.
Давайте сравним это изображение с изображением, которое имеет фактический размер 300 x 200 пикселей, но сохранено с качеством 80%.
Большинство пользователей не смогут заметить разницу между этими изображениями, но изображение слева имеет размер 21 КБ, а изображение справа — 7 КБ.
Есть две проблемы:
- кто решит сохранить картинку, тот сохранит ее в плохом качестве
- браузеру придется увеличивать или уменьшать изображения
В итоге их домашняя страница с 40 изображениями, выполненными в такой технике, заняла 450 КБ.
Впечатляющий! Вот еще одна хорошая техника.
У нас есть изображение и нам нужно уменьшить его размер.
Что заставит его лучше сжиматься? Контраст! Что, если вы удалите его или значительно уменьшите, а затем вернете с помощью CSS-фильтров? Но опять же, каждый, кто захочет скачать эту картинку, столкнется с плохим качеством.
С помощью этой техники можно добиться отличных результатов.
Вот некоторые примеры:
Все бы ничего, но как насчет дополнительных задержек рендеринга? Ведь браузеру приходится применять к изображению фильтры.
Но здесь все вполне позитивно: 27 мс против 23 мс без использования фильтров — разница незначительна.
Фильтры поддерживаются везде, кроме IE.
Какие еще техники существуют? Сравните две фотографии:
Отличие — размытие несущественных деталей на фотографии, что уменьшает размер до 147 Кб.
Но этого недостаточно! Перейдем к кодированию JPEG. Допустим, у вас есть последовательный и прогрессивный JPEG.
Последовательный JPEG загружается на страницу построчно, прогрессивный - сначала в плохом качестве весь сразу, а потом качество постепенно улучшается.
Если посмотреть на то, как работают кодеры, можно увидеть несколько уровней сканирования.
В этом файле можно найти множество различных уровней сканирования.
Наша цель, как разработчиков, — сразу показать подробную информацию об этой картинке.
Тогда мы сможем убедиться, что Ships Fast и Showsскоро имеют какие-то коэффициенты, которые могут лучше соответствовать картинке, и тогда уже на первом уровне мы увидим структуру, а не просто что-то размытое.
А на втором – почти все.
Существуют библиотеки и утилиты, позволяющие проделывать такие трюки: Adept, mozjpeg или Guetzli.
Уровень 3
Помню лет семь-десять назад — захотел шрифты, добавил font-face и всё.
А сейчас нет, мне нужно подумать, что я хочу сделать и как мне это нужно загрузить.
Так какой же лучший способ загрузить шрифты?
Мы можем использовать синтаксис font-face, чтобы избежать распространенных ошибок:
Если мы хотим поддерживать только более-менее нормальные браузеры, то можно написать еще короче:
Что происходит, когда у нас есть этот шрифт в CSS? Браузеры смотрят, есть ли ссылка на шрифт в теле или где-то еще, и если есть, то браузер начинает ее загружать.
И нам придется подождать.
Если шрифты еще не кэшированы, они будут запрошены, загружены и применены, что задержит рендеринг.
Но разные браузеры действуют по-разному.
Существуют подходы к картированию FOUT и FOIT.
FOIT (Flash of Invisible Text) — ничего не отображается, пока не загрузятся шрифты.
FOUT (Flash Unstyled Text) — контент отображается сразу со шрифтами по умолчанию, а затем загружаются необходимые шрифты.
Обычно браузеры ждут загрузки шрифтов три секунды, а если они не успевают загрузиться, то подставляются шрифты по умолчанию.
Есть браузеры, которые не ждут. Но самое неприятное, что есть браузеры, которые ждут до конца.
Так не пойдет! Есть много разных вариантов обойти это.
Одним из них является API загрузки шрифтов CSS. Создайте новый шрифт в JS. Если шрифты загружены, то вешаем их в соответствующие места.
Если не загружаются, вешаем стандартные.
Мы также можем использовать новые свойства в CSS, такие как Font-Rendering, что позволяет нам эмулировать либо FOIT, либо FOUT, но на самом деле они нам даже не нужны, потому что есть опция Font Rendering.
Есть еще один способ — критический FOFT с Data URI. Вместо загрузки через API JavaScript веб-шрифт встраивается непосредственно в разметку в виде встроенного URI данных.
Двухэтапный рендеринг: сначала латинский шрифт, а затем остальные:
- Загрузите полные шрифты со всеми весами и стилями.
- Минимальный подмножество шрифтов (A–Z, 0–9, знаки препинания)
- Используйте sessionStorage для повторных посещений.
- Сначала загрузите нижний индексный шрифт (римский)
Более того, этот метод имеет самую быструю стратегию загрузки шрифтов на сегодняшний день.
Я думал, что смогу добиться еще большего.
Вместо использования sessionStorage мы встраиваем веб-шрифт в разметку и используем Service Workers. Например, у нас есть какой-то шрифт, но весь он нам не нужен.
И мы не просто делаем подмножество, а выбираем то, что нужно для данной страницы.
Например, возьмите курсив, уменьшите его, сначала загрузите, отобразите на странице, и он будет выглядеть как обычный, жирный будет выглядеть как обычный, все будет выглядеть как обычно.
Дальше все загружается как надо.
Далее делаем подмножество и отправляем его Service Workers. Затем, когда пользователь впервые заходит на страницу, мы проверяем наличие шрифта; если его нет, то мы сразу отображаем текст, асинхронно загружаем этот шрифт и добавляем его в Service Workers, короче.
Когда пользователь входит в систему во второй раз, шрифт уже должен быть в Service Workers. Далее проверяем, есть ли он там и если есть, то сразу берем его оттуда, а если нет, то все эти действия повторяются снова.
Здесь есть проблема с кэшированием.
Какова вероятность того, что кто-то зайдет на ваш сайт и там будут все файлы, которые должны быть в кеше?
На изображении выше показаны результаты исследования 2007 года, в котором говорится, что 40-60% пользователей имеют пустой кеш, а 20% всех просмотров страниц происходят с пустым кешем.
Почему это? Потому что браузеры не умеют кэшировать? Нет, мы просто посещаем большое количество сайтов, и если бы все было в кэше, память ПК или смартфона заполнилась бы очень быстро.
Браузеры удаляют из кеша то, что они считают более ненужным.
Давайте рассмотрим на примере Chrome, что в нем происходит, когда мы пытаемся открыть любую страницу в Интернете.
Если вы посмотрите на строку шрифтов, то увидите, что шрифты попадают в кеш памяти или кеш HTTP в лучшем случае в 70% случаев.
На самом деле это неприятные цифры.
Если шрифты каждый раз загружаются заново, каждый раз пользователь заходит на сайт и наблюдает изменение стиля шрифта.
С точки зрения UX это не очень хорошо.
Необходимо позаботиться о том, чтобы шрифты действительно оставались в кеше.
Раньше мы полагались на локальное хранилище, но теперь имеет больше смысла полагаться на Service Workers. Потому что если я что-то положу в Service Workers, то оно там будет. Что еще можно сделать? Вы можете использовать диапазон Юникода.
Многие думают, что происходит динамическое подмножество, то есть у нас есть шрифт, он динамически разбирается, и загружается только указанная часть в unicode-диапазоне.
На самом деле это не так, и загружается весь шрифт.
Действительно, это полезно, когда у нас есть диапазон юникода, например, для кириллицы и для английского текста.
Вместо загрузки шрифта с английским и русским текстами мы можем разделить его на несколько частей и загрузить русский, если на странице есть русский текст, и сделать то же самое с английским.
Что еще можно сделать? Есть классная штука, которую нужно использовать всегда и везде — предзагрузка.
Предварительная загрузка позволяет загружать ресурсы в начале загрузки страницы, что, в свою очередь, снижает вероятность блокировки рендеринга страницы.
Такой подход повышает производительность.
Мы также можем использовать font-display: необязательно.
Это новое свойство в CSS. Как это работает?
Font-Display имеет несколько значений.
Начнем с блока.
Это свойство устанавливает блокировку шрифта на три секунды, в течение которых шрифт загружается, затем шрифт заменяется и затем отображается напрямую.
Свойство swap работает примерно так же, за некоторыми исключениями.
Браузер сразу рисует текст запасным шрифтом, а при загрузке указанного происходит замена.
Fallback устанавливает небольшой период блокировки 100 мс, период замены составит 3 с, после чего шрифт будет заменен.
Если шрифт не был загружен в течение этого времени, браузер отобразит текст с использованием резервного шрифта.
И, наконец, мы подошли к опциональному.
Период блокировки – 100 мс; если за это время шрифт не был загружен, текст отображается сразу.
Если у вас медленное соединение, браузер может перестать загружать шрифт. Когда шрифт загрузится, вы все равно увидите шрифт по умолчанию.
Чтобы увидеть прописанный шрифт, необходимо перезагрузить страницу.
Уровень 4
Есть много техник, которые мы использовали до http/2, такие как конкатенация, спрайты и т. д. Но с появлением http/2 необходимость в их использовании отпала, так как в отличие от http/1.1 в новой версии загружается практически всё.
сразу, и это здорово, потому что вы можете использовать множество дополнительных функций.
Теоретически переход на http/2 обещает нам на 64% (23% на мобильных устройствах) более быструю загрузку страниц.
Но на практике все работает медленнее.
Если большая часть вашей целевой аудитории постоянно обращается к ресурсу, находясь в автобусе, машине и т. д., то вполне возможно, что http/1.1 окажется в более выигрышном положении.
Взгляните на результаты теста ниже.
Они показывают, что в некоторых ситуациях http/1.1 работает быстрее.
У http/2 есть замечательные возможности, например, HPACK, который нужно использовать всегда и везде, а также передача на сервер.
Но есть небольшая проблема.
Это происходит в зависимости от браузера и сервера.
Допустим, мы загружаем страницу, у нас нет никакой отправки на сервер.
Если страница перезагружается, то все в кеше.
Но если мы сделаем отправку на сервер, то наш css дойдет до пользователя гораздо быстрее.
Но это также означает, что даже если CSS находятся в кеше, они все равно будут отправлены.
То есть если вы запихнете с сервера много файлов, они будут скачаны много раз.
Вперед, продолжать.
Существуют некоторые рекомендуемые ограничения по времени загрузки страницы.
Например, для среднего Android-устройства это пять секунд. Это не так уж и много, если учесть, что у нас, например, есть 3G.
Если вы посмотрите на рекомендованный предел размера загружаемого файла, необходимый для начала рендеринга, который упоминает Google, он составляет 170 КБ.
Поэтому, когда дело доходит до фреймворков, нам нужно думать о парсинге, компиляции, качестве сети, стоимости времени выполнения и т. д.
Существуют различные варианты загрузки файлов, например, классический, немного устаревший метод — scout. Создаем файл scout.js, он в html, загружаем его.
Его задача — сделать остальную часть среды максимально кэшируемой и при этом оперативно сообщать об изменениях в ней.
Это значит, что этому файлу нужно некоторое время оставаться в кеше, и если что-то изменится в среде, то scout немедленно инициирует обновление.
Это эффективный метод, поскольку нам не нужно каждый раз загружать и заменять html. Что делать с http/2? Ведь мы знаем, что можем отправлять столько файлов, сколько захотим и нет необходимости объединять их в пакеты.
Давайте тогда загрузим 140 модулей, почему бы и нет? На самом деле это очень плохая идея.
Во-первых, если у нас много файлов и мы не используем для сжатия библиотеку типа gzip, то файлы будут больше.
Во-вторых, браузеры еще не оптимизированы для таких рабочих процессов.
В результате мы начали экспериментировать и искать подходящее количество, и оказалось, что оптимально отправлять около 10 пакетов.
Пакеты лучше упаковывать с учетом частоты обновления файлов: часто обновляются в одних пакетах и редко обновляются в других, чтобы избежать ненужных загрузок.
Например, библиотеки должны быть упакованы вместе с утилитами и т.п.
Ничего особенного.
Что делать с css, как его загрузить? Push-сервер здесь не будет работать.
В начале мы загружали все как минифицированные файлы, потом подумали, что некоторые из них нужно загружать в критический css, ведь у нас всего 14 КБ, и их нужно загружать как можно быстрее.
Мы начали создавать loadCSS, писать логику, затем добавили display:none.
Но все это выглядело как-то нехорошо.
В http/2 считали, что все файлы нужно разбивать, минимизировать и загружать.
Оказалось, что лучшим вариантом оказался тот, что на изображении ниже.
Необычный! Эта опция хорошо работает в Chrome, плохо в IE, в Firefox было немного медленнее, потому что изменили рендеринг.
Таким образом, мы улучшили скорость работы на 120 мс.
Если вы посмотрите на работу с прогрессивным CSS и без него.
С прогрессивным css все загружается быстрее, но частями, а без его использования медленнее, потому что.
css находится в шапке и блокирует страницу как js.
Уровень 5
И последний уровень, о котором я не могу не рассказать, — это Resource Hints. Это отличная функция, которая позволяет делать много полезных вещей.
Давайте пройдемся по некоторым из них.
Предварительная выборка
Prefetch — сообщает браузеру, что нам скоро понадобится тот или иной файл, и браузер загружает его с низким приоритетом.
<link rel="prefetch" href="(url)">
Пререндер
Предварительный рендеринг — этой функции больше не существует, но раньше она помогала предварительно визуализировать страницу.Возможно, у нее будет альтернатива.
<link rel="prerender" href="(url)">
DNS-предварительная выборка
Dns-prefetch также ускоряет процесс загрузки страницы.Использование dns-prefetch предполагает, что браузер предварительно загружает адрес сервера указанного доменного имени.
<link rel="dns-prefetch" href="(url)">
Предварительное подключение
Preconnect позволяет выполнить предварительное рукопожатие с указанными серверами.
<link rel="preconnect" href="(url)">
Предварительная загрузка
Предварительная загрузка — сообщает браузеру, какие ресурсы следует предварительно загрузить с высоким приоритетом.Предварительную загрузку можно использовать для скриптов и шрифтов.
<link rel="preload" href="(url)" as="(type)">
Помню, в 2009 году я прочитал статью " Gmail для мобильных устройств.
Серия HTML5: сокращение задержки при запуске ", и она изменила мои взгляды на классические правила.
Посмотрите сами! JS-код у нас есть, но весь он нам сейчас не нужен.
Так почему бы нам не закомментировать большую часть JS-кода, а потом, когда необходимо, раскомментировать и выполнить в eval?
И причина, по которой они это сделали, заключается в том, что среднестатистический смартфон имеет время анализа в 8-9 раз больше, чем последний iPhone.
Давайте посмотрим на статистику.
Чтобы разобрать 1 МБ кода на среднем телефоне, требуется 4 секунды.
Теги: #Разработка сайтов #Высокая производительность #JavaScript #веб-дизайн #holyjs #holyjs #Friedman
-
Как Мы Строили B2B
19 Oct, 24 -
Выделенный Сервер За 1500 Рублей.
19 Oct, 24 -
Веб-Аналитика На Кибориф
19 Oct, 24