Критерии производительности пользовательского интерфейса для веб-дизайнеров и фронт-энд разработчиков.

Перевод статьи:  Front-end performance for web designers and front-end developers.
Автор:  Harry Roberts.

Очень трудно, если не сказать невозможно, отрицать тот факт, что производительность является одним из критических аспектов любого достойного проекта, будь то небольшой сайт-портфолио, ориентированное на мобильные устройства веб-приложение или что-либо другое в плоть до полномасштабного коммерческого проекта. Отовсюду — в процессе обучения, из статей и благодаря личному опыту мы в который раз узнаем — чем быстрее, тем лучше.

Производительность — это не только невероятно важно, это еще и чертовски интересно. То, что меня все больше и больше увлекает как в работе (я постоянно допекаю нашего главного инженера по контролю за производительностью), так и в своих проектах, а также в CSS Wizardry (Не даю покоя Энди Дейвису).

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

Этот сам по себе огромный пост не содержит уйму сбивающих с толку диаграмм и чисел, требующих от вас сложных аналитических размышлений, а как раз наоборот, затрагивает теорию производительности и непосредственные технологии ее реализации, к которым я пришел самостоятельно в процессе чтения, наблюдений, и которые являются плодом моих личных проб и ошибок, а также совместных работ (я провел массу времени анализируя диаграммы, так называемые «водопады» производительности своего сайта CSS Wizardry). Эта публикация также познакомит вас с другими статьями на похожие темы, которые позволят укрепить знания по необходимым ключевым вопросам. Что ж, наслаждайтесь!

Необходимо обратить внимание на то, что данная статья потребует от вас лишь небольшой объем предварительных базовых знаний о веб-производительности, тем не менее, если вам встретится что-нибудь незнакомое, следует просто отвлечься и воспользоваться поиском Google.

Основы.

Существует несколько связанных с производительностью моментов, которые, вероятней всего, уже известны всем дизайнерами и фронт-энд разработчикам. К ним можно отнести рекомендации по: выполнению минимально возможного количества запросов; оптимизации изображений; помещению таблиц стилей непосредственно в исходный документ, а точнее в его элемент <head>; расположение скриптов до закрывающего тега тела документа </body>; минимизацию JavaScript и CSS кода; и т.д. Соблюдая эти основные положения вы уже будете на правильном пути к повышению скорости взаимодействия с вашими пользователями, однако, это еще не все, далеко не все.

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

Стили сверху, скрипты снизу.

Это на самом деле основополагающее правило, которое к тому же в большинстве случаев очень легко соблюдать. Но почему это так важно? Выразимся очень кратко:

  • CSS блокирует визуализацию контента, поэтому все связанные со стилями вопросы вы должны поставить на первый план (то есть поместить в начале документа, в элемент <head>).
  • JavaScript блокирует процесс загрузки, а значит их нужно запускать в последнюю очередь, чтобы избежать возникновения каких-либо задержек обработки других компонентов страницы.

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

Итак, с этим понятно, вы просто размещаете свои CSS настройки в верхней части страницы, позволяя браузеру обработать их в первую очередь.

JavaScript является препятствием для загрузки по нескольким причинам (хотя здесь браузер тоже ведет себя разумно), однако сначала давайте разберемся с тем, как на самом деле происходит загрузка ресурсов в браузере. Говоря простыми словами, браузер может выполнять параллельную загрузку максимально возможного количества ресурсов из одного домена. Чем больше задействовано доменов, тем больше ресурсов может быть загружено параллельно за один промежуток времени.

При этом JavaScript прерывает этот процесс, блокируя параллельную загрузку от любого и всех задействованных при этом доменов, вот по каким причинам:

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

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

Но современные браузеры, несомненно, поступают более разумно. Здесь я приведу выдержку из адресованного мне Энди Дэйвисом (Andy Davies) сообщения, поскольку он объясняет это как-то лучше, чем я:

Современные браузеры выполняют загрузку JavaScript параллельно, блокируя при этом процесс визуализации лишь до тех пор, пока скрипт не будет выполнен (хотя вполне очевидно, что он тоже должен быть загружен).

Зачастую загрузкой скрипта занимается модуль предварительной загрузки браузера pre-loader.

В период блокирования браузером процесса визуализации страницы (к примеру, во время ожидания загрузки CSS или выполнения скрипта), спекулятивный (или опережающий) парсер (pre-parser) сканирует оставшуюся часть документа на предмет ресурсов, которые тоже могут быть загружены.

В некоторых браузерах (к примеру, в Chrome) предусмотрена приоритезация загружаемых ресурсов. Так, допустим, если загрузки ожидают скрипт и изображение, то в первую очередь будет загружен скрипт.

Ну что ж, неплохо придумано.

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

Делайте меньше запросов.

Другой, на самом деле очевидный способ оптимизации производительности, это, собственно, снижение количества самих загрузок. Каждый задействованный на странице ресурс требует отдельный HTTP запрос, а браузер отвлекается и занимается извлечением из сети каждого такого ресурса, используемого в отображаемой странице. Не каждый запрос проходит гладко, он может вызвать процесс разрешения доменного имени, редирект, искомый ресурс может просто отсутствовать. Каждый инициируемый вами HTTP запрос, для чего бы он ни предназначался: загрузки таблицы стилей, изображения, шрифта, файла JavaScript, …назовите здесь что угодно, представляет собой потенциально дорогостоящую операцию. Минимизация количества этих запросов является самым быстрым способом оптимизации производительности, из всех доступных для вас.

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

Добивайтесь максимальной параллелизации.

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

Многие сайты предусматривают наличие статических/ресурсных доменов. Взять, к примеру, Twitter, который для загрузки статических ресурсов использует si0.twimg.com:

<link rel="stylesheet" href="https://si0.twimg.com/a/1358386289/t1/css/t1_core.bundle.css" type="text/css" media="screen">

У Facebook для этих целей имеется fbstatic-a.akamaihd.net:

<link rel="stylesheet" href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yi/r/76f893pcD3j.css">

Задействуя такие статические/ресурсные домены, Twitter и Facebook могут предоставлять больше ресурсов в параллель, т.е. объекты из twitter.com и si0.twimg.com могут загружаться совместно. Это действительно простейший способ достижения одновременных загрузок, необходимых для полного представления вашей страницы. Еще большего эффекта можно добиться используя этот подход совместно с актуальной на данный момент технологией CDN (*Content Distribution Network — Сеть доставки контента), которая позволяет уменьшить латентность путем размещения необходимых ресурсов на более предпочтительных в плане географического расположения серверах.

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

Итак, выделим наши основы производительности в отдельный список:

  • Размещайте таблицы стилей в верхней части документа.
  • JavaScript необходимо размещать внизу (где это возможно).
  • Делайте как можно меньше HTTP запросов.
  • Задействование нескольких доменов для предоставления ресурсов документа может повысить количество ресурсов, загружаемых браузером параллельно.

HTTP запросы и разрешения доменных имен.

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

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

И если каждый запрашиваемый вами домен заведомо требует выполнения процесса разрешения доменного имени службой DNS, то вы должны быть на сто процентов уверены в том, что это действительно стоит того. В случае с необольшим сайтом (типа CSS Wizardry, к примеру) задействование поддомена для загрузки ресурсов будет неоправданным шагом. По всей вероятности браузер сможет быстрее справиться с этой задачей путем параллельной загрузки нескольких объектов из одного домена, чем делать это предварительно инициировав DNS поиск с последующей параллелизацией загрузок.

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

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

Важно также помнить о том, что поскольку запрос HTML кода производился, к примеру, из домена foo.com, процесс поиска данного доменного имени уже был выполнен ранее и все последующие запросы по этому домену больше не нуждаются в обращении к службе DNS.

Упреждающее разрешение доменных имен.

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

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

В максимальной степени проблема DNS поиска проявляется при задействовании сторонних доменных имен. К счастью, существует супер быстрый и простой способ ускорения этого процесса: "DNS prefetching" (*предварительное разрешение доменных имен).

DNS prefetching делает именно то, о чем гласит сам термин этого процесса, к тому же он невероятно прост в применении. Если вам необходимо произвести выборку объектов из, скажем, widget.foo.com, то лучше всего воспользоваться возможностью предварительного разрешения DNS путем простого добавления следующего кода непосредственно в элемент <head> вашей страницы:

<head>

<link rel="dns-prefetch" href="//widget.foo.com">

</head>

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

Этот простой элемент link обладает полной обратной совместимостью и не оказывает негативного влияния на производительность. Более того, его вполне можно рассматривать как элемент прогрессивного улучшения производительности.

Предварительная выборка ресурсов.

Так же как и упреждающий DNS поиск, не менее полезным может быть предварительная выборка необходимых для вашего сайта ресурсов. Для того, чтобы определиться с тем, какие именно объекты необходимо загружать заранее, мы должны знать как и когда браузер запрашивает ресурс при обычных обстоятельствах.

Веб-шрифты и изображения, на которые ссылается CSS код, ведут себя в данном случае одинаково. Браузер начинает их загрузку сразу же при встрече фрагмента HTML кода, в котором эти ресурсы запрашиваются. Как я уже упоминал ранее, браузеры очень «разумны», и вот еще одно свидетельство этого. Только вообразите себе ситуацию, если бы пользовательские агенты загружали указываемые в приведенных ниже CSS декларациях изображения незамедлительно после их обнаружения:

.page––home { background-image:url(home.jpg); }
.page––about { background-image:url(about.jpg); }
.page––portfolio { background-image:url(portfolio.jpg); }
.page––contact { background-image:url(contact.jpg); }

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

Если мы абсолютно уверены, что определенное CSS изображение должно постоянно использоваться на каждой странице сайта, то можно перехитрить браузер, вынудив его загружать требуемое изображение заранее, т.е. до того, как браузер начнет обработку HTML кода, в котором оно используется. Это невероятно просто сделать, однако в зависимости от того, какой способ вы выберете, ваши действия могут быть в некоторой степени нелегальными.

«Грязный» и похоже самый отказоустойчивый способ заключается в использовании на каждой странице скрытого <div>, в котором содержатся CSS изображения в виде <img> элементов с пустыми alt атрибутами. Именно так я поступаю со спрайтом на "CSS Wizardry", и поскольку я уверен, что он будет использоваться на каждой странице сайта, без колебаний обеспечиваю предварительную загрузку данного спрайта, ссылаясь на него из HTML кода. Трюк заключается в способе обработки браузерами внутристрочных элементов изображений <img>, который хорош тем, что предусматривает предварительную выборку и доставку содержимого таких элементов на самом раннем этапе обработки страницы. Таким образом я заставляю браузер загружать мой спрайт в качестве <img> элемента разметки, что гарантирует его доставку до того, как он понадобится CSS коду. Путем ссылки на скрытый спрайт в моем HTML я подталкиваю браузер к его загрузке.

Что касается второго, более легального метода, конструкция которого, кстати, очень похожа на упреждающее разрешение доменных имен, то он связан с некоторой путаницей:

<link rel="prefetch" href="sprite.png">

Таким способом мы сообщаем браузеру, что он должен начать предварительную выборку спрайта независимо от решения, которое может быть принято в результате анализа CSS кода.

Неопределенность заключается в очевидном несоответствии концепций, высказанных в двух опубликованных ранее статьях. В первой статье из MDN утверждается, что такая конструкция должна рассматриваться браузером как подсказка к возможной предварительной загрузке объекта, если браузер бездействует. В противоположность этому другая статья из Planet Performance высказывает мнение, что если браузер допускает присутствие атрибута rel="prefetch", то он должен выполнять предварительную выборку соответствующего ресурса независимо от своего состояния. Графики производительности, так называемые "водопады", которые я просмотрел, говорят в пользу последнего утверждения. Однако учитывая невозможность просмотра упреждающей выборки в WebKit при открытой консоли Developer Tools (что связано со странностью данного движка), стопроцентной уверенности у меня нет. Буду очень рад любым пояснениям по данному вопросу с вашей стороны.

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

<link rel="prefetch" href="webfont.woff">

Другими словами, все чем мы здесь занимаемся, так это вводим в заблуждение браузер, заставляя его загрузить объект заранее. В результате чего когда дело доходит до применения нашего CSS кода, необходимый ресурс будет уже доставлен или, по крайней мере, будет в пути. Ловко, верно?

CSS и производительность.

В большинстве рекомендаций утверждается, что если в вашем распоряжении имеются домены для ресурсов, то все статические объекты должны доставляться именно с них, при этом речь также идет и о таких ресурсах как CSS, JavaScript, изображения и т.п.

Единственное, о чем мы можем говорить с вами уверенно, так это то, что не нужно использовать дополнительные поддомены для доставки CSS документов.

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

У нас на работе мы создаем сайт на платформе, которая предусматривает доставку всех необходимых объектов с одного хоста (к примеру, foo.com). Однако когда дело дошло до так называемого «оживления» платформы, то для размещения всех ресурсов нам пришлось использовать несколько доменов (s1.foo.com и s2.foo.com). А это значит, что все изображения, JavaScript, CSS, шрифты и т.п. поступали из разных доменов, т.е. DNS поиска в этом случае не избежать. Здесь проблема заключается в том, что поскольку кэш изначально пуст, инициация процесса разрешения доменных имен, необходимая для получения требуемых CSS файлов, напрямую приводит к замедлению прохождения критического пути. И диаграммы наглядно это демонстрируют фрагментами резкого падения производительности, чего теоретически быть не должно, так как согласно установившимся практическим рекомендациям, большое количество требуемых для загрузки объектов должно распределяться между несколькими поддоменами, верно? Да, но это не про CSS. На выполнение DNS поиска требуется существенный промежуток времени, который может ощутимо замедлить процесс отображения страницы.

Именно по причине блокировки визуализации, CSS является одним из злейших врагов производительности, что неплохо описал Стоян Стефанов. А от того, что перед тем как отображать страницу браузер загружает все CSS документы, в большинстве случаев толку мало. Поскольку это означает, что файл print.css будет загружен в любом случае, даже если страница лишь отображается браузером на экране устройства. Это также касается и любых CSS файлов, которые используются на основе медиа-запросов (типа <link rel="stylesheet" media="screen and (min-device-width: 800px)" href="desktop.css">), так как они тоже будут загружены, даже если в этих документах нет никакой необходимости.

Тем не менее, как я был осведомлен Энди Дейвисом (Andy Davies), на самом деле WebKit в процессе загрузки CSS файлов делает это на основе их приоритетности, то есть в первую очередь поступают те стили, которые необходимы для первоначальной визуализации страницы, а загрузка всех остальных документов (таких, к примеру, как print.css) откладывается на как можно поздний срок. Что ж, неплохо.

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

  • Никогда не размещайте их на отдельном, используемом для других ресурсов домене, поскольку это может стать причиной инициации DNS поиска, замедляющего дальнейшее отображение страницы.
  • Обеспечьте к ним ранний доступ, чтобы браузер мог работать без задержек.
  • Соединяйте их. Поскольку браузер все равно будет извлекать все CSS файлы, вам лучше смягчить наносимый по производительности удар, объединив их в единый HTML запрос.
  • Архивируйте их и делайте компактнее, так как это снизит объем загружаемых данных.
  • В конце концов, кэшируйте их, благодаря чему весь описанный выше процесс будет выполняться как можно реже.

CSS находится на вашем критическом пути, поэтому вам нужно на раннем этапе решить вопрос обеспечения браузера этим кодом. Он блокирует отображение документа, а значит замедляет и ухудшает процесс взаимодействия с вашими посетителями. Запомните — использование поддоменов для размещения CSS кода убивает вашу производительность.

Gzip архивирование и минификация.

Это два на самом деле простых способа, которые вы можете (должны) применять к своим текстовым ресурсам. Их минификация или минимизация подразумевает удаление любых комментариев и пробельного контента, а gzip архивирование позволяет сжимать эти объекты, упрощая тем самым их дальнейшую загрузку.

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

Задействование gzip архивирования потребует от вас определенных махинаций с файлом .htaccess. Однако как объяснил один из моих хороших друзей Ник Пэйн (Nick Payne), на самом деле .htaccess не работает на производительность серверной стороны, поскольку этот файл проверяется при каждом входящем запросе, а значит само его наличие уже подразумевает дополнительные расходы.

Следующая информация взята из документации Apache:

Если у вас есть доступ к основному файлу конфигурации httpd сервера, то вам следует вообще отказаться использования .htaccess. Поскольку файлы .htaccess замедляют работу http сервера Apache. Любую директиву, которую вы собираетесь включить в .htaccess, поместите в блок Directory. Эффект от этого будет тот же, а производительность при этом лишь выиграет.

Если же у вас есть доступ только к файлам .htaccess, то я бы тоже особо не беспокоился, так как расходы на дополнительную нагрузку на производительность не будут столь значительными, чтобы обращать на них внимание. Плюс здесь в простоте реализации gzip ахивирования с помощью .htaccess. Что касается минификации, то как мы уже упоминали, без нее можно обойтись. А если у вас есть инструмент компиляции типа CodeKit, который содержит препроцессор, напрямую минимизирующий вводимые данные, то это тоже не доставит вам особого беспокойства.

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

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

Как и любой другой компрессионный алгоритм, gzip сжимает поступающие на вход текстовые данные беря за основу повторяющиеся строки. Большая часть кода на самом деле достаточно эффективно сжимается с помощью gzip, поскольку любому коду свойственны повторяющиеся текстовые фрагметы и строки. К ним можно отнести имена свойств в CSS (к примеру, background-image) или элементы разметки в HTML (подобно сниппет, демонстрирующий насколько удачно со своей задачей справляется фреймворк HTML5 Boilerplate.

Компрессия контента реально дает огромную выгоду. Вот вам пример. В ходе разработки платформы inuit.css ее «сырой вес» составлял 77 килобайт. В результате gzip сжатия получилось всего 5,52 килобайта. Минимизация и gzip сжатие в комплексе дают до 93% уменьшения объема. И поскольку gzip прекрасно работает с текстовыми ресурсами, то вы можете с успехом использовать его для сжатия SVG или даже некоторых файлов шрифтов.

Оптимизация изображений.

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

Задействование спрайтов.

Если вы хотите иметь сайт с достаточно высоким уровнем производительности, то без спрайтов вам просто не обойтись. Ведь загрузка одного изображения большого размера за один HTTP запрос значительно предпочтительней нескольких небольших изображений, загружаемых посредством отдельного запроса на каждое из них. Тем не менее, проблема заключается в том, что не все изображения вот так вот просто можно взять и реализовать с помощью спрайта. Взять, к примеру, случай, когда иконка должна быть использована в качестве фона элемента с не фиксированной шириной в резиновом шаблоне. Здесь спрайт не сработает, поскольку они неприменимы к элементам с плавающими размерами. При разработке спрайта наличие вокруг изображения большого количества свободного пространства вполне допустимо, а что касается потраченных при этом впустую пикселей, то это уже вопрос эффективности таких спрайтов.

Для того чтобы бороться с неспособностью некоторых элементов работать со спрайтами, нам необходимо знать, что мы подразумеваем, когда говорим «задействованный спрайтом элемент». Это, в сущности, пустой элемент, как правило <i>, задача которого заключается лишь в том, чтобы оставаться всегда пустым и просто заполняться фоновым изображением.

Я применял спрайты при создании сайта Sky Bet. YouTube и Facebook тоже их используют. Создатель концепции SMACSS (Масштабируемая модульная архитектура CSS) Джонатан Снук (Jonathan Snook) посвятил целый раздел этому подходу.

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

<li>
<a href="/profile/">
<i class="icon icon—person"></i> Profile
</a>
</li>

В данной ситуации мы не можем задействовать спрайт в элементе <li> или <a>, однако для этого мы можем использовать пустой элемент <i>, который вполне может содержать иконку. Это один из моих любимых приемов, позволяющих повысить производительность. Здесь мы используем так называемую «плохую» разметку, которая в данном случае оправдана, поскольку ее целью является применение разумных технологий, повышающих скорость реакции страниц. Отлично сработано!

Retina-изображения.

У вас нет необходимости везде использовать retina-изображения. Такие высококачественные картинки содержат вчетверо большее количество пикселей в сравнении с их аналогами стандартного разрешения. В четыре раза больше — внушительный аргумент. Означает ли это реальное увеличение размера файла, передаваемого по сети. К счастью, нет. Здесь на ситуацию влияет собственная кодировка изображения, которая подразумевает четырех кратное увеличение пиксельного объема изображения только при его декомпрессии, так называемой «распаковки» непосредственно перед отображением в окне браузера. Т.е. имеем проблему с размещением этого количества пикселей в оперативной памяти компьютера.

Давайте поразмыслим над этим. Ретина изображения в большинстве случаев (хотя и не всегда) необходимы для предоставления пользователям мобильных устройств более четкого интерфейса. Однако следует учитывать, что размер памяти телефонов значительно ниже чем у других устройств. Изображения уровня Retina приводят к неоправданной перегрузке памяти устройств, размер ОЗУ которых не рассчитан на размещение файлов такого объема. Поэтому нужно дважды подумать, прежде чем повсеместно применять retina-изображения. Может следует прибегнуть к разумным компромиссам?

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

Вы, конечно, можете схитрить и пойти по другому пути, предоставляя изображения с повышенным на 1,5х разрешением (вместо 2х) для всех устройств, пытаясь таким образом показать картинки достаточно высокого качества каждой категории пользователей. Тем не менее, на мой взгляд, наиболее оптимальное решение заключается в максимально ограниченном использовании retina-изображений.

Если вам позволяет статистика, то вместо растровых изображений можете использовать SVG или шрифтовые иконки. Что касается меня, то на Wizardry я применяю SVG изображения, и это дает мне следующие преимущества:

  • Независимость от разрешения.
  • Возможность уменьшения.
  • Возможность сжатия.

На работе Мэт Ален (Matt Allen) сделал для нас шрифтовые иконки, которые могут работать совместно с использующим спрайт элементом, для создания совместимых с retina изображениями масштабируемых иконок.

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

Прогрессивный JPEG.

Одним из интереснейших аспектов производительности является «воспринимаемая производительность», т.е. кажущаяся производительность, которая не обязательно основана на реальных показателях, а отражает то, насколько быстрым сайт ощущается.

Скорее всего, каждому из вас хорошо знакома ситуация, при которой загрузка большого JPG изображения производится рывками. Сначала первая сотня пикселей, пауза, потом за ними прорисовываются еще пятьдесят, через какое-то мгновение еще две сотни пикселей, а затем «Бац!» и все изображение загрузилось.

Именно так, как правило, ведет себя базовое (или последовательное) JPG изображение — не очень приятное ощущение загрузки фрагментами. Путем перехода на прогрессивные JPG файлы, вы сможете сделать этот процесс более приятным для пользователя. В этом случае изображение появляется сразу целиком, однако очень зернистое, а потом его резкость улучшается до нормального. И хотя на первый взгляд такой вариант кажется несколько хуже, чем в первый (базовый JPG), однако на самом деле второй способ создает ощущение более быстрой загрузки, поскольку пользователь изначально может видеть общие формы изображения с последующим прогрессивным улучшением его качества. Не смотря на то, что прогрессивные изображения, как правило, немного объемнее своих базовых «коллег», но на практике они создают впечатление более быстрой загрузки.

Для получения прогрессивных JPG изображений вам нужно всего лишь отметить соответствующий чек-бокс при сохранении изображения в Photoshop для Web. Вот и все.

Откажитесь от изображений совсем.

Нет, ну конечно вместо применения промежуточных методов, таких как переход на спрайты, задействование SVG и исключение retina-изображений, лучше вообще отказаться от использования изображений. Если ваш дизайн может быть реализован на 100% с изображениями и на 75% исключительно с помощью CSS, то отдайте предпочтение второму варианту, ограничьтесь только CSS (если это не приведет к увеличению объема кода на сотни строк, естественно). Такой подход предусматривает потенциальное исключение дополнительных HTTP запросов, а также значительно облегчает дальнейшее обслуживание ресурса. Если вы на самом деле можете позволить себе обойтись без изображений, то попытайтесь сделать это.

Итоги.

Итак, у нас на вооружении есть множество (хотя и небольшое) методов, которые мы можем применить, чтобы заставить работать браузер эффективнее и тем самым ускорить фронт-энд оболочку нашего проекта. Зная некоторые тонкости функционирования браузера, мы реально можем манипулировать им в дальнейшем и сделать наши фронт-энд разработки еще активнее.

Я искренне надеюсь, что изложенная здесь масса информации сделала свою работу и в некоторой степени просветила вас, оставив в вашей памяти знания о тех вещах, о существовании которых вы ранее, возможно, и не подозревали. Было бы также неплохо, чтобы данная статья хотя бы отчасти повысила ваш интерес к вопросу производительности, как это произошло со мной.

В заключение хотелось бы выразить особую благодарность Нику Пейну (Nick Payne) и Энди Дейвису (Andy Davies) за помощь в прояснении некоторых моментов, касающихся изложенного здесь материала.

* Примечание переводчика.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *