Yahoo: лучшие способы ускорения сайта

Предлагаем Вашему вниманию перевод статьи Стива Содерса с Yahoo по улучшению производительности сайта путем правильного проектирования HTTP/HTML/CSS/JS.

В статье рассмотрены 14 весьма(!) полезных правил

В 2004 году я создал группу Exceptional Performance на Yahoo!. Тогда мы были небольшой командой, целью деятельности которой было улучшение производительности продуктов Yahoo!. Проработав бОльшую часть своей карьеры как инженер внутреннего интерфейса, я подошёл к этому, как к проекту по оптимизации кода: я очертил весь процесс работы сети, чтобы выявить те места, где можно достугнуть максимальных возможностей для повышения производительности. С тех пор наша цель --- обогащение опыта конечного пользователя; я измерил времена отклика в браузере при разных скоростях передачи данных. Данные этих исследований представлены на следующей диаграмме, отображающей HTTP-траффик сайта http://www.yahoo.com.

На представленной схеме первая строка ("html") --- представляет исходный запрос для документа HTML. Как видно, только 5% времени отклика конечного пользователя тратится на считывание документа: такой результат справедлив практически для всех сайтов. Из первой десятки сайтов Соединённых штатов все, кроме одного, использовали менее 20% общего времени отклика при загрузке HTML-документа. Остальные 80% уходили на загрузку содержания страницы. Этот факт предоставляет возможности для ускорения работы сайтов за счёт улучшения пользовательского интерфейса.

Есть три основные причины, почему стоит начинать именно с пользовательского интерфейса.

  1. Существует много возможностей улучшения пользовательского интерфейса. Если уменьшить его наполовину, количество откликов уменьшится на 40% и более, в то время как сокращение серверной части даст выигрыш менее, чем в 10%.
  2. Улучшения клентской части обычно требуют меньше времени и ресурсов, чем серверной (переработка архитектуры и кода приложения, поиск и оптимизация критических участков, добавление и настройка аппаратного обеспечения, распределение баз данных и т.п.).
  3. Настройка производительности клиента доказала свою работоспособность. Более 50 команд на Yahoo! уменьшили время отклика клиентской части за счёт наших методов зачастую на 25% и более.

Наше золотое правило производительности звучит следующим образом: оптимизируйте сначала производительность клиента, где тратится более 80% времени отклика конечного пользователя.

Уменьшайте число HTTP-запросов

80% времени отклика конечного пользователя тратится на отображение интерфейса. БОльшая часть времени связана с загрузкой всех компонентов страницы: рисунков, таблиц стилей, скриптов и т.д. Уменьшение числа компонентов, в свою очередь, ведёт к уменьшению числа HTTP-запросов, необходимых для отображения страницы. В этом и заключается ускорение.

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

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

CSS-спрайты --- предпочтительный метод снижения количества запросов изображений. Соберите все картинки на вашей странице в одну и используйте свойства background-image и background-position языка CSS для отображения нужной части изображения.

Встроенные изображения используют URL-схему data: для внедрения информации изображения в саму страницу. Это может увеличит размер вашего HTML-документа. Комбинируя встроенные изображения с (кэшированными) таблицами стилей --- это способ уменьшить число HTTP-запросов и избежать увеличения размера страниц.

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

Уменьшение количества HTTP-запросов для страницы --- это наиболее важное направление для улучшения производительности для новых посетителей. Как описано в блоге Tenni Theurer Browser Cache Usage - Exposed!, 40--60% ежедневных посетителей имеют пустой кэш. Сделать свою страницу быстрой для этих впервые зашедших пользователей --- это ключ к улучшению пользовательского опыта.

Использование сетей доставки контента

Близость пользователя к вашему веб-серверу сильно влияет на время отклика. Распределение содержимого по различным территориально рассредоточенным серверам приведёт к ускорению загрузки ваших страниц с точки зрения пользователя. Но с чего начать?

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

Помните, что 80--90% времени отклика конечного пользователя тратится на загрузку всех компонентов страницы: изображений, таблиц стилей, скриптов, flash и т.п. Это - золотое правило производительности, как показано в разделе (Yahoo) Важность клиентской производительности. Вместо того, чтобы заниматься сложным перепроектированием архитектуры вашего приложения, лучше рассредоточить статическую информацию. Это не только приводит в большему ускорению, но и проще благодаря сетям доставки контента.

Сеть доставки контента (content delivery network, CDN) --- это набор веб-серверов, распределённых в различных местоположениях с целью более эффективной доставки данных клиенту. Выбор конкретного сервера для отправки данных конкретному клиенту, как правило, основывается на степени их взаимной близости. Например, это сервер, оптимальный по числу транзитов или с наименьшим временем отклика.

Некоторые крупные компании интернета владеют своими собственными CDN, но дешевле пользоваться провайдерами CDN-серверов, такими как Akamai Technologies, Mirror Image Internet или Limelight Networks. Для развивающихся компаний и веб-сайтов стоимость подобного сервиса недопустимо высока, но если ваша целевая аудитория постоянно увеличивается и становится глобальной, то CDN необходимы для достижения низкого времени отклика. Перенесение статического содержимого Yahoo! с серверов веб-приложения на CDN улучшило время отклика клиента более чем на 20%. Переход на CDN требует минимальных изменений кода, но существенно повысит скорость загрузки вашего сайта.

Добавление заголовка с информацией об истечении срока действия (Expires Header)

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

Браузеры (и прокси-серверы) используют кэш, чтобы уменьшить количество и размер HTTP-запросов, позволяя быстрее загружаться веб-страницам. Веб-серверы используют заголовок со сроком действия в HTTP-ответе, чтобы сообщить клиенту, как долго кэшированный компонент будет актуален. Вот пример такого заголовка, показывающего браузеру, что содержимое не устареет до 15 апреля 2010 года:

Expires: Thu, 15 Apr 2010 20:00:00 GMT

Для Apache испоьзуйте директиву ExpiresDefault для срока действия относительно текущей даты:

ExpiresDefault "access plus 10 years"

Помните, что при использовании заголовка, срок действия которого достаточно велик, то после изменения компонента придётся изменить его имя файла. На Yahoo! мы часто делаем этот шаг частью процесса сборки: номер версии добавляется в имя файла с компонентом, например, yahoo_2.0.6.js.

Применение таких заголовков, как уже было упомянуто, приносит пользу только в случае, если пользователь посещает ваш сайт уже не первый раз, иначе они не повлияют на количество HTTP-запросов, так как соответствующий кэш будет пуст. Таким образом, эффективность этого метода зависит от того, как часто посетители будут заходить на ваши страницы, имея «полный кэш» (кэш, содержащий все компоненты страницы). Мы провели исследования We этого способа на Yahoo! и выяснили, что число просмотров страниц с полным кэшом составляет 75--85%. Используя заголовки с информацией об истечении срока действия, вы увеличиваете число компонентов, кэшированных браузером, которые затем будут использоваться вместо оригинальных версий, не обмениваясь ни одним байтом через сеть.

Сжатие компонентов

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

Начиная с версии HTTP/1.1 веб-клиенты указывают поддержку сжатия в заголовке Accept-Encoding запроса:

Accept-Encoding: gzip, deflate

Если веб-сервер увидит этот заголовок в запросе, то он может сжать ответ, используя один из предложенных клиентом методов. Сервер уведомит об этом клиента через заголовок Content-Encoding

Content-Encoding: gzip

Gzip --- это самый популярный и эффективный метод сжатия данных на сегодняшний день. Он был разработан организацией GNU и стандартизован в RFC 1952. Единственный альтернативный способ кодирования --- это deflate (?), но он менее эффективен и популярен.

Gzip-сжатие в целом снижает размер ответа примерно на 70%. Приблизительно 90% сегодняшнего интернет-траффика проходит через браузеры, поддерживающие gzip. Если вы используете Apache, модуль, отвечающий за gzip, зависит от версии: Apache 1.3 использует mod_gzip, а Apache 2.x --- mod_deflate.

Есть известные проблемы с браузерами и прокси-серверами, которые могут вызвать несоответствие между тем, что браузер ожидает и что получает, при сжатии содержимого. К счастью, число таких крайних случаев сокращается из-за уменьшения количества старых браузеров. Модули Apache помогают тем, что добавляют соответствующие заголовки автоматически.

Серверы выбирают, какие данные будут сжимать, на основании типов файлов, но обычно они слишком ограниченны в выборе объектов для сжатия. Большинство веб-сайтов сжимают HTML-документы. Кроме того, стоит также сжимать скрипты и таблицы стилей, но многие сайты упускают эту возможность. Фактически, нужно сжимать любую текстовую информацию, включая XML и JSON. Не нужно архивировать PDF-документы и изображения, потому что они уже сжаты: вы не только впустую потратите время процессора, но и можете увеличить размер по сравнению в первоначальным.

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

Помещайте таблицы стилей вверху страницы

Исследуя производительность Yahoo!, мы обнаружили, что перемещение тиблиц стилей в заголовок (HEAD) приводит к ускорению загрузки страниц. Дело в том, что в этом случае страница формируется последовательно.

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

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

В HTML-спецификации четко сказано, что стилевые таблицы должны быть расположены в заголовке страницы: "Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times." Ни одна из альтернатив: пустой белый экран или момент нестилизованного текста --- не стоят риска. Оптимальное решение --- следование HTML-спецификации.

Располагайте скрипты внизу страницы

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

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

Вторая проблема, связанная со скриптами, заключается в блокировке параллельной загрузки. Спецификация HTTP/1.1 предполагает, что браузер загружает не более двух компонентов одновременно в расчёте на один хост. Если вы загружаете изображения с нескольких хостов, может получиться больше двух параллельных скачек. (Я однажды заставил Internet Explorer загружать одновременно 100 изображений.) Однако, пока загружается скрипт, браузер не будет скачивать что-то ещё, даже с другого хоста.

В некоторых ситуациях не так просто расположить скрипты внизу. Если, к примеру, скрипт использует document.write, чтобы поместить часть содержимого на страницу, его нельзя переместить вниз. Бывают также крайние случаи; однако, зачастую есть способы обхода подобных ситуаций.

Обычно возникает альтернативное предложение --- использовать «отложенные» скрипты. Атрибут DEFER указывает браузерам, что скрипт не содержит команд document.write и можно продолжать отображение страницы. К сожалению, Firefox не поддерживает атрибут DEFER. В браузере Internet Explorer скрипт может быть отложен, но не на столько, на сколько хотелось бы. Вообще, если загрузка скрипта может быть отложена, то его можно поместить внизу страницы, что позволить загружаться быстрее.

Избегайте CSS-выражений

CSS-выражения --- это мощный (и опасный) способ динамической установки CSS-свойств. Они поддерживаются браузером Internet Explorer, начиная с 5-ой версии. К примеру, цвет фона может меняться каждый час следующим образом:

background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

Как вы видите, метод expression принимает код на JavaScript. Результат вычисления этого выражения присваивается свойству CSS. Имейте в виду, что метод expression игнорируется другими браузерами, таким образом он полезен при установке свойств в Internet Explorer (?)

Проблема этого метода в том, что выражения вычисляются гораздо чаще, чем этого от них ожидают многие пользователи: не только при загрузке или обновлении страницы, но и при её прокрутке и даже при перемещении курсора над окном браузера. Добавив счётчик, вы можете проследить, когда и как часто выполняются CSS-выражения. Подвигайте курсор над страницей и тут же получите более 10000 раз.

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

Делайте компоненты JavaScript и CSS внешними

Многие из этих правил производительности затрагивают тему управления внешними компонентами. Тем не менее, до подобных рассуждений стоит задаться более простым вопросом: ДОЛЖНЫ JavaScript и CSS храниться в отдельных файлах или быть встроены в страницу?

Использование отдельных файлов на практике обычно делает страницы быстрее, так как файлы JavaScript и CSS в этом случае кэшируются браузером. Когда код JavaScript и CSS встраивается непосредственно в HTML-документ, он загружается при каждом запросе этого документа. Это уменьшает количество запросов, но увеличивает размер документа; с другой стороны, если JavaScript и CSS хранятся в отдельных файлах, кэшируемых браузером, размер страницы уменьшается, не увеличивая число HTTP-запросов.

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

Многие веб-сайты попадают где-то посередине этих оценок. Лучшим решением будет вынсти скрипты и таблицы стилей во внешние файлы. Единственным замеченным мной исключением, когда встраивание было выгоднее, были главные страницы, такие как главная страница Yahoo! (http://www.yahoo.com) и My Yahoo! (http://my.yahoo.com). Главные страницы простматриваются редко (иногда --- всего один раз) за сессию, следовательно, для них более приемлимо вставить JavaScript и CSS в тело документа.

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

Уменьшайте число DNS-запросов

Система доменных имён (Domain Name System, DNS) связывает символьные имена машин (hostname) и их IP-адреса (аналогично телефонному справичнику). Когда вы набираете в строке адреса www.yahoo.com, DNS-сервер, к которому обращается браузер, возвращает ему IP-адрес этого узла. Этот процесс занимает обычно 20--120 миллисекунд. Браузер не может загружать что-либо с данного узла, пока DNS-запрос не будет выполнен и не вернёт нужный адрес.

Для лучшей производительности DNS-запросы кэшируются, например, на специальном сервере, поддерживаемом провайдером, а также (для лучшей производительности) на локальных машинах пользователей. Информация DNS остаётся в кэше DNS операционной системы («DNS Client service» в Microsoft Windows). Большинство браузеров имеют свой собственный кэш, отделённый от системного; это позволяет не обращаться лишний раз к операционной системе.

Internet Explorer хранит кэшированные DNS-запросы 30 минут по умолчанию. Это значение хранится в реестре в параметре DnsCacheTimeout. Firefox кэширует DNS-запросы каждую минуту, что указано в параметре network.dnsCacheExpiration. (Fasterfox увеличивает его до одного часа.)

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

Уменьшая количество хостов ведёт к уменьшению распараллеливания загрузки страницы. Чем меньше происходит запросов, тем меньше становится время отклика, но, уменьшая параллельную загрузку, вы, наоборот, его увеличиваете. Я предпочитаю вариант, когда компоненты распределены на 2, 3 или 4 узла, благодаря чему можно достичь компромисса между малым числом DNS-запросов и высокой степенью распараллеливания закачки.

Минимизируйте скрипты JavaScript

Суть состоит в уменьшении размеров скриптов за счёт удаления ненужных символов (например, пробелов, символов новой строки и табуляции) из их кода. Это приводит к умешьнению времени отклика. Наиболее популярными средствами в этой области являются JSMin и YUI Compressor.

Есть и другой способ оптимизации кода JavaScript. Как и прошлый, он также предполагает удаление комментариев и лишних пробельных символов, но ещё и модифицирует сам код. В частности, это проявляется в изменении имён переменных и функций так, чтобы они содержали минимальное число символов, т.е. код становится более компактным, но читать его гораздо сложнее. Здесь вопрос выбора подходящего средства остаётся открытым, но наиболее часто (насколько мне известно) используется Dojo Compressor (ShrinkSafe).

Минимизация --- это безопасный и довольно простой процесс. Оптимизация кода, с другой стороны, является более сложной вещью и, следовательно, приводящей к большему числу ошибок; она также зависит от выявления имён API-функций, имена которых трогать не следует. Вдобавок становится затруднительной отладка оптимизированного кода. Я не видел проблем, связанных с минимизацией, но при применении оптимизации они встречались. В ходе исследования первой десятки американских веб-сайтов выяснилось, что минимизацию используют 21%, а оптимизацию --- 25%. Несмотря на преимущества второго способа, я всё-таки советую остановиться на минимизации, что уменьшит риски и сложность поддержки скриптов.

В дополнение к минимизации внешних скриптов, ту же технику можно и нужно применять к встроенным. Даже если вы сжимаете их gzip-ом, как описано в правиле 4, минимизация уменьшит размер на 5% и более. Это особенно актуально в условиях роста использования и размеров скриптов JavaScript.

Избегайте переадресаций

Переадресация выполняется с использованием кодов статуса 301 и 302. Вот пример HTTP-заголовков в отклике с кодом 301:

HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html

Браузер автоматически переходит по ссылке, указанной в поле Location. Вся необходимая для перенаправления информация содержится в заголовках. Тело ответа обычно пусто. Несмотря на названия, ни 301-й ни 302-й ответы не кэшируются на практике, если только это явно не указывается в дополнительных заголовках, например Expires или Cache-Control. Тэг meta refresh и JavaScript являются альтернативными способами перенаправить пользователя на другой URL, но если вы должны сделать переадресацию, то предпочтительнее будет использовать стандартные коды статуса 3xx HTTP, в первую очередь, для обеспечения корректной работы кнопки «Назад».

Главное помнить, что переадресация замедляет работу пользователя. Вставляя перенаправление между пользователем и HTML-документом, вы замедляете всё на странице, так как ничто на ней не будет формироваться и никакие компоненты не станут загружаться, пока не поступит HTML-документ.

Одна из наиболее бесполезных переадресаций часто возникает и веб-разработчики обычно о ней не беспокоятся. Так происходит, когда забывают вставить завершающий слэш (/) в URL, когда тот необходим. Например, если зайти на http://astrology.yahoo.com/astrology, результатом будет ответ с кодом 301 301, перенаправляющий на http://astrology.yahoo.com/astrology/ (заметьте добавленный слэш). Эта ошибка исправляется сервером Apache с помощью Alias или mod_rewrite или директивы DirectorySlash, если вы используете обработчики Apache.

Еще одно традиционное использование перенаправления --- соединение старой и новой версий сайта. Некоторые используют соединения с различными частями сайта в зависимости от некоторых условий (типа браузера, типа аккаунта и т.п.). Объединение двух веб-сайтов в один с помощью перенаправлений достаточно просто и почти не требует написания дополнительного кода. Несмотря на то что разработчики таким образом упрощают себе задачу, подобный подход отрицательно влияет на продуктивность работы сайта и, соответственно, конечного пользователя. Среди альтернатив можно выделить использование Alias и mod_rewrite, если оба проекта находятся на одном сервере. Если изменится имя домена, то можно использовать CNAME (DNS-запись, создающая alias, указывая с одного домена на другой) вместе с Alias или mod_rewrite.

Удаляйте дубликаты скриптов

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

Ненужные HTTP-запросы встречаются в Internet Explorer, но не в Firefox. В Internet Explorer, если внешний скрипт включён дважды и не кэшируется, создаются два HTTP-запроса во время загрузки страницы. Даже если скрипт закэширован, всё равно возникают дополнительные HTTP-запросы, когда пользователь обновляет страницу.

Помимо генерации лишних запросов время тратится на многократное выполнение скрипта. Это излишнее выполнение скриптов характерно и для Firefox, и для Internet Explorer, причём независимо от того, кэшируется ли он или нет.

Один из способов избежать случайного включения одного скрипта дважды --- реализовать модуль управления скриптами в вашей системе шаблонов. Традиционный способ подключения скрипта --- использовать тэг SCRIPT в вашей станице:

script type="text/javascript" src="menu_1.0.17.js"></script>

Альтернативой в PHP будет создание функции insertScript:

<?php insertScript("menu.js") ?>

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

Настраивайте тэги содержимого

Тэги содержимого (Entity tags, ETags) --- это механизм, который используют серверы и браузеры для определения, совпадает ли компонент в кэше браузера с тем, что находится на сервере. (Под содержимом имеются в виду изображения, скрипты, таблицы стилей и т.п.) Эти тэги используются для обеспечения механизма проверки содержимого, что более гибко по сравнению с датой последней модификации. Они представляют собой строку, которая однозначно определяет версию компонента. Единственное ограничение на формат --- заключённая в кавычки строка. Сервер определяет тэг компонента, используя заголовок ETag:

HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195

Далее, если браузеру требуется провериться компонент, он использует заголовок If-None-Match, чтобы отправить ETag обратно серверу. В случае совпадения тэга возвращается код 304, уменьшая ответ на 12195 байт для этого примера:

GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"
HTTP/1.1 304 Not Modified

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

Формат ETag для Apache 1.3 и 2.x --- inode-size-timestamp. Хотя один и тот же файл может находиться в одном каталоге на нескольких серверах и иметь одини и те же размер, права, время создания, модификации и т.п., его inode могут различаться.

У IIS 5.0 и 6.0 дела с ETags обстоят так же. Фотрматом для ETags в IIS служит Filetimestamp:ChangeNumber. ChangeNumber --- это счётчик, используемый для отслеживания изменений конфигурации IIS. Маловероятно, что ChangeNumber один и тот же на всех IIS-серверах, поддерживающих веб-сайт.

В итоге, ETags, сгенерированные Apache и IIS для одного компонента, не будут совпадать на разных серверах. В таком случае пользователь не получит быстрый ответ с кодом 304, для чего и были созданы эти тэги; вместо него сервер вернёт обычный ответ с кодом 200 со всеми данными компонента. Если ваш сайт располагается на одном сервере, то такой проблемы не возникнет; в противном случае, при использовании Apache или IIS с конфигурацией тэгов содержимого ETags по умолчанию, пользователи получат медленные страницы, серверы будут больше загружены, трафик увеличен и прокси не будут кэшировать содержимое должным образом. Даже если заголовок Expires компонентов содержит значение далеко в будущем, условный запрос GET будет генериться как только пользователь нажмёт кнопку обновления страницы.

Если вы не используете преимуществ гибкой модели валидации, которую обеспечивают тэги содержимого, лучше вообще их удалить. Заголовок Last-Modified осуществляет проверку на основании временнЫх отметок компонента. Удалив ETags, вы уменьшите размер заголовка ответа и последующих запросов. Статья Microsoft Support article описывает, как их удалить. В Apache это делается просто добавлением следующей строки к файлу конфигурации:

FileETag none

Делайте Ajax-элементы кэшируемыми

Люди спрашивают, применимы ли эти правила улучшения эффективности к приложениям Web 2.0. Конечно! Это правило было первым, которое принесло плоды после начала работы приложений Web 2.0 на Yahoo!.

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

Чтобы улучшить производительность, важно оптимизировать эти ответы. Основной способ добиться хорошей производительности Ajax --- кэширование, как описано в правиле 3 (добавление заголовка Expires). Некоторые другие правила также применимы к Ajax:

Однако, правило 3 является основным для разгона приложения. Посмотрим пример. Почтовый клиент на технологии Web 2.0 может использовать Ajax, чтобы загрузить адресную книгу пользователя для автопродолжения. Если пользователь не изменял её с прошлой загрузки, то адресную книгу можно взять из кэша, если прошлый ответ Ajax был кэшируемым с использованием заголовка Expires. Браузер должен знать, когда использовать ранее кэшированную адресную книгу, а когда загрузить с сервера новую. Это можно сделать, добавив временнУю отметку к URL книги, например, &t=1190241612. Если она не изменялась с момента последней загрузки, то отметка будет той же самой и адресная книга будет взята из кэша браузера. Если же пользователь изменил адресную книгу, то отметка времени модификации не совпадёт с той, что соответствует кэшу и браузер запросит обновлённые данные.

Даже если ответы Ajax создаются динамически и применяются только к одному пользователю, они всё ещё могут кэшироваться. Подобная техника делает приложения Web 2.0 быстрее.