Практический CSS/JS: архивируем все!

Примечание: ниже частичный перевод статьи "Compress JavaScript and CSS without touching your application code", в которой описывается статичное сжатие CSS- и JS-файлов на сервере и корректная выдача их затем клиенту. Далее даны мои комментарии с более комплексным решением. Приношу извинения, если для кого-то тема будет слишком знакома или неинтересна: в Рунете нормальной статьи на данную конкретную тематику обнаружить не удалось.

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

  • Проверить, умеет ли клиент принимать файлы в формате gzip-encoded.
  • Обеспечить соответствующий вывод на стороне сервера через gzip-функции, либо уповать на то, что всем этим займется непосредственно Apache.
  • Поиграться с .htaccess, чтобы обеспечить корректный content type.

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

Однако, существует способ обойтись просто парой строчек в конфиге (.htaccess), если потратить пару минут и самому заархивировать все необходимые файлы. Предположим, что у нас есть JS-библиотека prototype.js на сервере. Заархивируйте ее через gzip (при помощи 7-zip или еще чего-нибудь, если вы работаете под Windows). В итоге, у вас должен появиться файл prototype.js.gz. Поожите его в ту же директорию на сервере, что и исходный файл. Если вы работаете через командную строку в шеле, то достаточно выполнить:

gzip foo.js -c > foo.js.gz

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

AddEncoding gzip .gz
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{HTTP_USER_AGENT} !Safari
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [QSA,L]

В первой строке мы сообщаем серверу, что файлы с расширением .gz нужно отдавать с gzip encoding-type, чтобы браузер понял, что перед ним архив, а текстовый файл. Второй строкой проверяется, принимает ли браузер архивированные файлы, при этом следующие строки просто не отработают, если этот тест провалится. Далее мы исключаем Safari, у которого проблемы с правильным восприятием архивированного содержимого. На четвертой строке мы проверяем, что архивный файл существует, и если это так, то добавляем .gz к существующему имени файла.

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

Примечание: далее идут мои рассуждения на тему и собственное решение.

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

Во-вторых, мне не нравится, что при каждом запросе (ну или при 99%) к статичному ресурсу, Apache должен что-то искать и менять физический адрес, по которому этот файл нужно брать. Я против таких решений, поэтому я предложу другой путь: давайте будем переопределять физический адрес ресурса только для «старых» браузеров, а для всех остальных отдавать рядовой файл.

RewriteEngine On
AddEncoding gzip .gz
RewriteCond %{HTTP:Accept-encoding} !gzip [OR]
RewriteCond %{HTTP_USER_AGENT} Safari [OR]
RewriteCond %{HTTP_USER_AGENT} Konqueror
RewriteRule ^(.*).gz(?.+)?$ $1 [QSA,L]

<FilesMatch .*.js.gz$>
ForceType text/javascript
Header set Content-Encoding: gzip
</FilesMatch>
<FilesMatch .*.css.gz$>
ForceType text/css
Header set Content-Encoding: gzip
</FilesMatch>

В итоге, логика «переворачивается», и вместо набора «И»-условий мы получаем «ИЛИ»-условия. Первые пять строк уже достаточно подробно обсуждены вверху, я только остановлюсь на последней из них

RewriteRule ^(.*).gz(?.+)?$ $1 [QSA,L]

В ней учитывается, что к статичным ресурсам можно добавлять уникальные GET-строки, чтобы перегружать кеш на клиенте (если ресурс поменялся, то стоит обновить его в кеше, иначе клиенты увидят старую версию). Например, foo.css.gz?v1234.

Следующие строки (ForceType / set Content-Encoding) форсируют для скриптов и файлов стилей соответствующие MIME-типы и Content-Encoding (ибо как удалось нарыть здесь, так браузеры воспринимают ответ лучше). Единственное неудобство, которое может возникнуть — придется поменять все вызовы JS-/CCS-файлов на аналогичные с .gz, но если изначально работать с ними, то ничего менять и не придется. При изменении самих ресурсов нужно будет снова их заархивировать и поменять строку вызова в HTML-файлах (чтобы избежать кеширования). При промышленном подходе к разработке, все эти действия автоматизируются, а при кустарном — трудозатраты не так существенны по сравнению с увеличением скорости загрузки сайта (если, конечно, не собирать проект прямо на боевом сайте, без конца gzip'я один и тот же файл :).

Старая версия правил (без учета Konqueror'а) тестировалась здесь, полет нормальный.

Спасибо всем, кто дочитал до конца. Буду рад комментариям и добавлениям.

Автор перевода: http://sunnybear.habrahabr.ru/



Опубликовал admin
14 Янв, Понедельник 2008г.



Программирование для чайников.