Журнал «Компьютерра» № 20 от 30 мая 2006 года - Компьютерра. Страница 29
# map of where we can find .js source files after the build process
# has merged as necessary
$GLOBALS[‘config’][‘js_source_map’] = array(
‘foo.js’ => ‘foobar.js’,
‘bar.js’ => ‘foobar.js’,
‘baz.js’ => ‘baz.js’,
function smarty_insert_js($args){
if ($GLOBALS[‘config’][‘is_dev_site’]){
$files = explode(‘,’, $args[‘files’]);
$files = array();
foreach (explode(‘,’, $args[‘files’]) as $file){
$files[$GLOBALS[‘config’][‘js_source_map’][$file]]++;
$files = array_keys($files);
foreach ($files as $file){
echo «<script type=\»text/javascript\" src=\"/javascript/$file\"></script>n";
<script type="text/javascript" src="/javascript/foobar.js"></script>
<script type="text/javascript" src="/javascript/baz.js"></script>
Исходный код шаблонов не меняется, что позволяет нам сохранять эти файлы разделенными во время разработки. Кроме того, мы можем написать собственный процесс объединения на PHP и использовать тот же самый конфигурационный блок при самом объединении (а использование одного и того же конфигурационного файла избавляет нас от необходимости синхронизации). А если брать по максимуму, то можно проанализировать использование скриптов и стилей на страницах сайта, чтобы определить, какие именно файлы лучше объединять (хорошие кандидаты для такого объединения – это файлы, которые почти всегда подгружаются вместе).
В случае с CSS можно начать с полезной модели, состоящей из двух стилей: основного стиля и стиля подраздела. Единый основной стиль используется во всем приложении, а разные стили подразделов контролируют разные функциональные области. В этом случае подавляющее большинство страниц подгружают только два стиля (один из которых кэшируется при первой загрузке – это, конечно, основной).
Для небольших наборов CSS и JavaScript такой подход может замедлить обработку первого запроса (в сравнении с «монолитным» подходом), но если сохранять количество компонентов на относительно низком уровне, то возможно и ускорение работы, поскольку в расчете на одну страницу приходится загружать меньше данных.
Когда речь заходит о сжатии, многие немедленно вспоминают mod_gzip. Однако с ним нужно соблюдать осторожность – mod_gzip, в общем-то, является злом или, по меньшей мере, причиной кошмарного расходования ресурсов. Основная идея сжатия проста. Браузеры, запрашивая ресурсы, указывают в заголовке, в каком виде они могут принять содержимое страницы. Это может выглядеть, например, так:
Accept-Encoding: gzip,deflate
Видя такой заголовок, сервер сжимает отсылаемое содержимое методами gzip или deflate с тем, чтобы клиент распаковал его на месте. Это загружает процессор как на клиенте, так и на сервере, однако уменьшает объем передаваемых данных. Это нормально. Однако вот как работает mod_gzip: он сжимает данные во временный файл на диске, а после отсылки данных удаляет этот файл. На больших системах вы очень быстро столкнетесь с ограничениями скорости ввода/вывода. Этого можно избежать, используя вместо mod_gzip mod_deflate (только в Apache 2), поскольку последний сжимает данные в оперативной памяти. Пользователи первой версии Apache могут создать RAM-диск и хранить временные файлы mod_gzip там – это не так быстро, как работа напрямую с оперативкой, но намного шустрее, чем писать все на жесткий диск.
Но даже без этого мы можем полностью избежать вышеописанных проблем, используя предварительное сжатие нужных статических ресурсов и последующую отправку их клиенту с помощью mod_gzip. Если добавить предварительное сжатие в сборку билда, то сам процесс пройдет для нас совершенно незаметно. Файлов, требующих упаковки, обычно не так уж много – мы не сжимаем изображения (поскольку это вряд ли приведет хоть к какой-то экономии – изображения и так уже достаточно сжаты), так что нам остается упаковать JavaScript, CSS и прочий статический контент. Но мы должны указать mod_gzip, где искать файлы для предварительной компрессии:
mod_gzip_can_negotiate Yes
mod_gzip_static_suffix .gz
AddEncoding gzip .gz
В последних версиях mod_gzip (от 1.3.26.1a и выше) для автоматической предварительной упаковки файлов в конфигурационных опциях достаточно добавить одну строчку. Нужно лишь удостовериться, что в Apache установлены корректные разрешения на создание и перезапись упакованных файлов.
mod_gzip_update_static Yes
Но не все так просто. Текущие версии Netscape 4 (точнее, 4.06–4.08) в заголовке утверждают, что понимают содержимое, сжатое с помощью gzip, однако, на самом деле, не умеют распаковывать эти архивы. Прочие версии Netscape 4 тоже испытывают разнообразные трудности с загрузкой сжатых скриптов и стилей. А значит, мы должны отсекать этих агентов на стороне сервера и подставлять им неупакованный контент. Это довольно просто. Куда интереснее проблемы, возникающие у Internet Explorer (версии с 4 по 6).
Загружая сжатый с помощью gzip JavaScript, Internet Explorer порой некорректно распаковывает его или прерывает распаковку в процессе, отдавая клиенту половину файла. Если для вас критична работоспособность JavaScript, вы должны избегать отсылки сжатых скриптов по запросу IE. Даже в тех случаях, когда IE способен загрузить сжатый JavaScript, он зачастую не кэширует его, независимо от указаний, записанных в тегах (актуально для некоторых версий IE 5.x).
Поскольку сжатие с помощью gzip иногда выходит себе дороже, мы можем обратиться к другим способам упаковки контента, не предполагающих смену формата. Сейчас доступно множество скриптов, сжимающих JavaScript, большая часть которых использует для уменьшения исходного кода наборы правил на основе регулярных выражений. С их помощью мы действительно можем сделать код меньше, удалив комментарии, лишние пробелы, сократив имена переменных и убрав необязательные синтаксические конструкции.
К сожалению, подавляющее большинство этих скриптов либо не слишком эффективны, либо в определенных случаях разрушают код (а иногда – и то и другое вместе). Без подробного грамматического разбора упаковщику трудно отличить комментарий от похожей конструкции, размещенной в закавыченной строке. Кроме того, с помощью регулярных выражений не так-то просто оценить, какая из переменных имеет ограниченный контекст, так что некоторые техники сокращения имен переменных могут разрушить сам код.
Этих проблем можно избежать, сжимая код с помощью Dojo Compressor (alex.dojotoolkit.org/shrinksafe), использующий Rhino (мозилловский JavaScript-движок, написанный на Java) для построения дерева, которое оптимизируется перед работой с файлами. С работой Dojo Compressor справляется неплохо, ресурсов отнимает немного. Расширив наш процесс сборки билда с помощью этого инструмента, мы можем забыть об экономии, писать пространные комментарии, вставлять сколько угодно пробелов и т. д. На рабочем коде это нисколько не отразится.
По сравнению с JavaScript CSS упаковывать легко. Поскольку в стилях практически не используются закавыченные строки (как правило, это пути или названия шрифтов), мы можем справиться с пробелами с помощью регулярных выражений. Если же у нас закавыченные строчки все же есть, мы почти всегда можем свести последовательности пробелов к одному пробелу (маловероятно, что последовательности, состоящие из нескольких пробелов, встретятся нам в указаниях путей или названиях шрифтов). Для этого нам вполне хватит простенького скрипта на Perl:
#!/usr/bin/perl
my $data = ‘’;
open F, $ARGV[0] or die «Can’t open source file: $!»;
$data .= $_ while <F>;
$data =~ s!/*(.*?)*/!!g; # remove comments
$data =~ s!s+! !g; # collapse space
$data =~ s!} !}n!g; # add line breaks
$data =~ s!n$!!; # remove last break