В последние годы в сети появились две тенденции:
- Веб-страницы становятся все больше и больше с точки зрения потребляемой пропускной способности и загруженных ресурсов (CSS, JS, изображения, шрифты).
- Доля пользователей, которые пользуются интернетом с помощью мобильных телефонов и планшетов, растет.
Конечно, дома у вас есть настольный компьютер с очень быстрым интернет-соединением. Тем не менее, многие люди, вероятно, не посещают ваш сайт с такой же настройкой. Даже хорошее соединение 4G на дороге только решает проблему доступной пропускной способности. По-прежнему остаются проблемы сетевых издержек и задержки мобильной связи.
Это привело к совершенно новому взгляду на скорость и оптимизацию страниц. В настоящее время Google оценивает страницы выше, если они отображаются менее чем за секунду. Предполагается, что это оптимальное время отклика, учитывая, что 10 мс воспринимается как мгновенная реакция, 3 с позволяет пользователям дрейфовать, а 10 с — причина никогда больше не посещать ваш сайт.
Но как вы можете достичь этой амбициозной цели, не отключая функции и не нанося существенного вреда вашему сайту?
Идея состоит не только в том, чтобы разместить ваши страницы на приличном сервере и максимально снизить вес сайта, но и в том, чтобы оптимизировать цепочку событий, которые происходят для отображения вашего веб-сайта в окне браузера. Эта цепочка называется « Критический путь рендеринга» .
От загрузки страницы к отображению страницы
Google определил критический путь рендеринга как последовательность шагов, которые проходит браузер, чтобы превратить «код и ресурсы, необходимые для рендеринга первоначального представления веб-страницы», в реальные пиксели на экране.
Это полностью меняет понятие скорости страницы. Недостаточно загружать целую страницу как можно быстрее. Важнейшей частью является обеспечение мобильных пользователей быстрой видимой реакцией, чтобы гарантировать бесперебойную работу. Здесь критический термин является ключевым, поскольку не все в вашем коде необходимо для создания чего-либо в окне браузера.
Чтобы оптимизировать критический путь и, следовательно, скорость рендеринга страницы, давайте посмотрим на упомянутые шаги и как они взаимодействуют друг с другом.
От пустого к содержанию
Представьте, что пользователь посещает наш (по общему признанию простой) веб-сайт. После ввода URL-адреса браузер отправляет запрос на сервер и в итоге получает следующий HTML-код.
<!DOCTYPE html> <html lang="en"> <head> <title>Sample Site</title> <link href="style.css" rel="stylesheet"> </head> <body> <p> Hello <span>there</span> SitePoint! <img src="photo.jpg"> </p> </body> </html>
Браузер анализирует этот поток байтов кода в объектную модель документа или DOM . DOM является полным представлением дерева разметки HTML:
Примечание . Браузер создает DOM постепенно . Это означает, что он может начать анализировать HTML, как только появятся первые фрагменты кода, и затем будет добавлять узлы в древовидную структуру по мере необходимости. Это может быть использовано для некоторых продвинутых, но эффективных оптимизаций (которые выходят за рамки этой статьи).
На данный момент окно браузера все еще пустое. Обратите внимание, как на файл CSS ссылаются внутри <head>
. Стиль сайта определенно важен для рендеринга и поэтому должен быть загружен, как только анализатор HTML достигнет <link>
.
p { font-weight: bold; } p span { display: none; }
Этот небольшой файл CSS затем анализируется в объектной модели CSS или CSSOM .
К сожалению, CSSOM не может быть построен постепенно, как DOM. Представьте, что в таблице стилей есть третья строка, например, p { font-weight: normal; }
p { font-weight: normal; }
, который переопределяет первое объявление p
. Это демонстрирует, что мы должны ждать, пока весь CSS не будет загружен и обработан, прежде чем мы сможем перейти к шагу рендеринга. CSS это блокировка рендера .
Как только браузер имеет машиночитаемое представление разметки и CSS, он может построить дерево рендеринга . Эта структура объединяет DOM и CSSOM, захватывая только видимые элементы:
Обратите внимание, что тег span
не является частью дерева рендеринга (еще одна причина, по которой CSS блокирует рендеринг; только CSS в конечном итоге сообщает браузеру, что должно быть частью дерева рендеринга).
Дерево рендеринга — это хорошее представление контента, который мы позже увидим в окне браузера. Фактические пиксели появляются после двух оставшихся шагов: Layout и Paint . Шаг макета обрабатывает вычисление положения и размера каждого элемента по отношению к текущему окну просмотра. Наконец, браузер раскрашивает все и отображает всю визуализированную страницу.
При каждом изменении дерева рендеринга (например, с помощью интерактивного JavaScript) или области просмотра (например, путем изменения размера окна браузера или вращения вашего мобильного устройства) необходимо выполнить повторный запуск макета и рисования.
Полный критический путь рендеринга для этого простого примера выглядит следующим образом:
Как насчет изображения?
На самом деле, только HTML, CSS и JavaScript считаются критическими ресурсами (то есть ресурсами, которые могут блокировать рендеринг страницы). Оказывается, изображения ни блокируют построение DOM, ни начальную визуализацию страницы. Взгляните на сетевую временную шкалу нашего простого веб-сайта, предоставленного Chrome DevTools:
Синяя вертикальная полоса отмечает событие DOMContentLoaded
. Это событие вызывается, как только DOM готов. Изображение загружается позже, что доказывает, что изображения не блокируют синтаксический анализ — или даже рендеринг, в этом отношении. Они блокируют событие Load (красная вертикальная линия). Это событие указывает на то, что все ресурсы, необходимые для запрашиваемой страницы, были загружены и обработаны. При этом загрузка изображения, конечно, должна быть оптимизирована. Но изображения не являются критическими с точки зрения пути рендеринга.
Приправить вещи с помощью JavaScript
JavaScript является мощным инструментом и оказывает огромное влияние на критический путь. Давайте расширим наш тег абзаца встроенным скриптом:
<p> Hello <span>there</span>, SitePoint! <script> document.write('How are you?'); var color = elem.style.color; elem.style.color = 'red'; </script> <img src="photo.jpg"> </p>
Даже этот простой фрагмент кода демонстрирует, что сценарии могут как запрашивать, так и изменять DOM, а также CSSOM. (Для простоты предположим, что elem
содержит ссылку на какой-то конкретный элемент HTML на странице.) Поскольку JavaScript может добавлять вещи в DOM (например, текстовый узел «Как дела?»), Парсер должен остановить его работать, пока скрипт не будет полностью выполнен. JavaScript блокирует парсер .
Вторая строка JavaScript выше запрашивает цвет элемента. Следовательно, CSSOM должен присутствовать до запуска скрипта. Поэтому CSS также является блокировкой скриптов .
Давайте посмотрим, как выглядит критический путь рендеринга, когда мы избавимся от встроенного скрипта и заменим его ссылкой на внешний файл JavaScript:
<!DOCTYPE html> <html lang="en"> <head> <title>Sample Site</title> <link href="style.css" rel="stylesheet"> </head> <body> <p> Hello <span>there</span> SitePoint! <img src="photo.jpg"> </p> <script src="app.js"></script> </body> </html>
Внешний файл JavaScript требует дополнительного запроса — встроенному скрипту это бы не понадобилось. Кроме того, не позволяйте блок-схеме обманывать вас: независимо от того, какой файл, CSS или JS, появляется первым в браузере, CSS должен быть проанализирован первым. Как только CSSOM присутствует, содержимое скрипта может быть выполнено. Только после этого DOM-парсер разблокируется и может завершить свою работу. Даже на нашем довольно простом примере с веб-сайтом скрывается серьезная блокировка и вмешательство.
Теперь, когда мы знаем, какие шаги необходимы для отображения страницы и как критические ресурсы — HTML, CSS и JavaScript — пересекаются друг с другом, мы можем получить некоторые меры по оптимизации.
Три шага к оптимизации критического пути рендеринга
Есть три места, где вы можете оптимизировать критический путь рендеринга и, следовательно, скорость, с которой браузер дает видимый результат для пользователя. Имейте в виду, что оптимизация — это все о тщательных измерениях и компромиссах. К сожалению, никогда не бывает правильного ответа, подходящего для всех сценариев. Не стесняйтесь использовать такую помощь, как Chrome DevTools, чтобы увидеть, что происходит.
1. Минимизируйте количество байтов, которые идут вниз по сети
Это может быть легко, но вы будете удивлены тем, сколько веб-сайтов все еще не следуют простейшему правилу оптимизации скорости: чем меньше ваш сайт, тем быстрее он загружается и рендерится. Это так просто!
Сократите , сожмите и кэшируйте ваши активы, а также ваш HTML. Да, HTML также блокирует рендеринг . Многие инструменты сборки, такие как Grunt или Gulp , поставляются с плагинами для минимизации / удаления. Они очищают ваш код от пробелов, комментариев и многого другого, чтобы подготовить ваши файлы к работе.
Сжатие уменьшает загрузку вашей страницы еще больше, в то время как кэширование избавляет браузер от сетевых обращений к серверу и обратно. Вместо того, чтобы запрашивать каждый критический ресурс при каждой перезагрузке страницы, он может использовать кэшированную локальную копию.
Примечание. Если вы уменьшите размер своего HTML-кода, вы рискуете некорректно отобразить содержимое и стили. Например, если вы используете естественный пробел для разделения элементов встроенного блока, этот пробел исчезнет при минификации. Так что используйте HTML минификацию осторожно.
2. Минимизируйте блокировку рендеринга CSS
Наш любимый язык стилей является важной частью каждого пути рендеринга. Помните, что CSS блокирует рендеринг и выполнение JS. Неудивительно, что золотое правило для CSS таково: передавайте CSS пользователю как можно быстрее и быстрее! Убедитесь, что все теги CSS- link
находятся в head
вашего HTML-кода, чтобы браузер мог немедленно отправлять запросы.
Другая стратегия состоит в том, чтобы уменьшить количество блокирующих визуализацию CSS с помощью медиа-запросов . Скажем, наш пример веб-сайта имеет стили для печати, а также специальные правила для устройств в ландшафтном режиме. Вы можете использовать эти знания, чтобы разбить CSS на несколько файлов и позволить браузеру анализировать их условно.
<!DOCTYPE html> <html lang="en"> <head> <title>Sample Site</title> <link href="style.css" rel="stylesheet"> <link href="print.css" rel="stylesheet" media="print"> <link href="landscape.css" rel="stylesheet" media="orientation:landscape"> </head> ...
Исходный файл style.css теперь должен быть значительно меньше и является единственным ресурсом CSS, который всегда блокирует рендеринг. Таблица стилей печати используется только для печати, а не для отображения страницы в браузере. Таким образом, это не блокировка рендера. Третий файл CSS имеет динамический медиа-запрос, и он определяется по загрузке страницы и в зависимости от ориентации устройства, является ли оно блокировкой рендеринга или нет.
По-видимому, мы можем использовать медиа-запросы для того, чтобы сделать внешний вид CSS-кода, который необходим только при особых обстоятельствах или условиях. Это уменьшает размер файла нашего основного файла основного стиля и, следовательно, время, которое требуется браузеру для его анализа и продолжения процесса рендеринга.
Примечание . Браузер все равно будет загружать все таблицы стилей, но это происходит с более низким приоритетом и параллельно с процессом рендеринга.
И последнее, но не менее важное: вы также можете встроить CSS для рендеринга и вставить его прямо в HTML-файл. Это освобождает браузер от сети и обеспечивает обработку только HTML-файла (и, возможно, сценариев).
<!DOCTYPE html> <html lang="en"> <head> <title>Sample Site</title> <style> p { font-weight: bold; } p span { display: none; } </style> <!-- No link tag needed! --> </head> ...
Как упоминалось ранее, это все о компромиссах. Вышеприведенный трюк может принести вам меньшую задержку в сети, но потенциально может увеличить общую нагрузку на случай, если критический CSS-код будет вставлен во многие файлы.
Есть инструменты, которые помогут вам сделать это автоматически. Вот несколько вариантов:
- Critical Path CSS Generator , который также можно использовать как модуль Node или задачу Grunt .
- критический
- CriticalCSS
3. Минимизируйте блокировку парсера JavaScript
То же самое верно и для JavaScript: если вам нужны некоторые строки кода для первоначального рендеринга страницы, вы можете включить их в свой HTML-файл, чтобы сохранить обходы по сети.
В оптимальном случае вы отбрасываете JavaScript все вместе для первоначального отображения страницы. Если это невозможно, попробуйте отложить выполнение JavaScript и отложить его до события загрузки.
Кроме того, тщательно проверяйте свои скрипты или сторонние плагины. Если они никак не взаимодействуют с DOM и CSSOM, велика вероятность, что вы можете загрузить их асинхронно или кратко асинхронно . Достичь этого легко:
... <script src="app.js" async></script> </body> </html>
Сделав это, вы только что сказали браузеру, что все в порядке, чтобы не выполнять скрипт в той точке, на которую он ссылается в документе HTML. Это позволит браузеру продолжить сборку DOM и выполнить сценарий, когда DOM будет готов. Представьте, что app.js содержит только установочный код для Google Analytics и несколько функций социальных сетей. Если код оставляет DOM и CSSOM нетронутыми, он является идеальным кандидатом для асинхронного выполнения и разблокирования синтаксического анализа.
Вывод
Большая часть исследований по этой части была взята из нескольких разных источников:
- Отличная документация Google по основам Web , и, в частности, раздел о критическом пути рендеринга .
- Курс по оптимизации производительности сайта Udacity.
- Бесплатная онлайн книга « Высокопроизводительные браузерные сети » Ильи Григорика
Так что не забудьте проверить эти источники для более подробной информации о материалах, описанных выше.
Для краткого изложения этой статьи, здесь приводится TL; DR:
- Понятие скорости страницы перешло от простой загрузки страницы к отображению страницы.
- Critical Rendering Path включает в себя все этапы превращения критических ресурсов в видимый вывод браузера: DOM и CSSOM, JavaScript, дерево рендеринга, макет и фаза рисования.
- HTML это блокировка рендера, но DOM может быть построен постепенно
- CSS — это блокировка рендеринга и сценариев, осторожно относитесь к ним и оптимизируйте их с помощью встроенных стилей или медиазапросов.
- JS блокирует синтаксический анализатор, использует его экономно во время начальной загрузки страницы, откладывает выполнение или пытается загрузить его асинхронно
- Не забывайте, что размер по-прежнему имеет значение, уменьшайте, сжимайте, кешируйте
Не бойтесь запускать такие инструменты, как Chrome DevTools , PageSpeed Insights или WebPagetest, чтобы узнать, что происходит до того, как ваш веб-сайт отображается.
Скорость рендеринга вашего сайта крайне важна для ваших посетителей, которые обычно не относятся к вашим страницам с терпением. Имейте это в виду, когда вы настраиваете производительность своего сайта в полной мере. Это действительно полезно!