Автоматизируем клиентскую оптимизацию

Предыстория

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

Проблема в том, что, согласно принципам, описанным в статье Best Practices for Speeding Up Your Web Site (перевод доступен на сайте webo.in), для ускорения загрузки сайта нам нужно произвести следующие манипуляции над javascript и css файлами:

  1. Слить весь javascript в один файл, причем, желательно так, чтобы сохранился нужный порядок – т.е., скажем, библиотека jQuery – была ближе к началу, а функции и объекты, которые ее используют – после нее.
  2. Слить весь css в один файл
  3. Сжать эти большие файлы с помощью какой-нибудь утилиты вроде yui-compressor (за исключением css-файлов, название которых начинается, скажем, с префикса ie_, которые содержат data:URL, и поэтому критично относятся к переходам со строки на строку, так что их для собственного спокойствия лучше не сжимать)
  4. Расположить их в таком порядке – css-файл как можно ближе к открывающему тэгу head, а js-файл – как можно ближе к закрывающему тэгу body.
  5. Выставить HTTP-заголовок expires на подольше, чтобы браузер пользователя их закешировал. Ну а для того, чтобы при следующем билде у пользователя обновился js и css надо этим файлам дать какое-нибудь уникальное имя.
  6. Перед отдачей файлов клиенту сжимать их с помощью gzip

К чему это я?

Пункты 5 и 6 уже подробно расписаны в других местах.
Я же хочу рассмотреть в этой статье вопрос автоматизации пунктов 1,2,3,4. А точнее, я хочу предложить инструмент, с помощью которого одним (ну, максимум – двумя-тремя :) нажатием кнопки можно выполнить пункты 1, 2, 3, 4 настоящего списка и получить готовые к заливке на сервер javascript и css файлы.

Инструментарий

  • Apache Ant в качестве сборщика. Выбор пал на него за скорость работы, доступность, кроссплатформенность, а также то соображение, что получившийся скрипт легко включить в более общий скрипт выкладывания проекта на production, который будет выполнять какой-нибудь CI-tool, скажем, teamcity.
  • YUI Compressor в качестве утилиты сжатия js и css файлов. Взят за кроссплатформенность, адекватность, хорошую скорость работы
  • JSLint4Java (порт JSLint на java) в качестве валидатора javascript. Мы же не хотим выкладывать нерабочий код на продакшен, верно? Кстати, фраза, написанная на офф. сайте, «JSLint may hurt your feelings» очень даже справедлива :)

Алгоритм работы скрипта

  1. Скачиваем и распаковываем JSLint4Java и YUI Compressor, кладем их в папочку tools внутри проекта. Правильнее, конечно, ложить ее куда-нибудь в место, определенное системной переменной, что-нибудь вроде $TOOLS_LOCATION, но в демонстрационном скрипте и так сойдет, а уж вы для себя поправьте скрипт, как вам нужно.
  2. Натравливаем JSLint4Java на все js-файлы. Если JSLint находит какую-нибудь ошибку, выводим ее на экран и останавливаем выполнение скрипта.
  3. Сливаем все js-файлы, за исключением тех, в имени которых есть фраза test, в один файл с уникальным именем, при этом сохраняем порядок следования файлов, который мы где-нибудь в другом месте определим. В качестве уникального имени давайте возьмем такую конструкцию: main.hh.dd.MM.yy.js, где hh, dd, MM, yy, соответственно, текущие час, день, месяц, год.
  4. Сливаем все css-файлы, за исключением тех, имя которых начинается с ie_ в один файл с уникальным именем (имя такое же, как и в предыдущем пункте, только расширение сменится на `css`). Порядок следования в данном случае не важен.
  5. Натравливаем на получившиеся файлы YUI Compressor. Если при сжатии произошла ошибка, выводим на экран ошибку и останавливаем выполнение скрипта.
  6. В html-темплейте, который подключает все файлы стилей, удаляем все тэги link, кроме тех, в src которых прописаны файлы ie_ и тех, которые содержат правила стилей, а не подключают внешний css-файл при помощи атрибута src.
  7. В том же темплейте удаляем все тэги скрипт, кроме тех, которые содержат javascript-код (а не подключают внешний файл скрипта через атрибут src).
  8. В том же темплейте прописываем получившийся css-файл как можно ближе к открывающему тэгу head.
  9. В том же темплейте прописываем получившийся js-файл как можно ближе к закрывающему тэгу body или открывающему тэгу script получившийся js-файл.. Такое странное поведение нужно вот для чего: предположим, что мы в js-файле прописали какие-то библиотечные функции, а прямо в html-файле инициализируем прямо в server-side коде js-объекты какими-то данными. Вот для этого-то нам и нужно сохранить script-тэг, а также подключить получившийся js-файл до него.
  10. Выкладываем получившиеся js, css, html файлы в какую-нибудь директорию.

Пример реализации

На всякий случай: пример

TODO

  1. Не слишком красивым является указание порядка конкатенации js-файлов прямо в скрипте. Гораздо лучше было бы, если бы мы писали прямо в html что-нибудь вроде . При development build’е скрипт бы заменял эту строку на несколько script-tag’ов (удобно для debug-a), а на продакшене сливал бы файлы в один в указанном порядке.
  2. Скачивание файлов при помощи ant-а не сказать, чтобы очень удобное – а что, если выйдет новая версия или изменится ссылка на скачивание? Гораздо удобнее пользоваться maven‘ом для таких случаев.
  3. Дополняйте :)

3 комментария to “Автоматизируем клиентскую оптимизацию”

  1. Здравствуйте, Александр!
    В первую очередь хотел бы поблагодарить Вас за этот прекрасный блог. Узнаю много нового и полезного с ним =)

    Теперь к делу: Как вы сами относитесь к сообщениям JSLint? На что обращаете внимание, на что забиваете?
    //Это я к «JSLint may hurt your feelings». Ругается, причем довольно обильно, на вполне нормальные файлы, которые работают под тремя браузерами.
    Боттом-лайн проверки prototype.js:
    jslint:<…>\ant\js\prototype.js:228:46:Too many errors. (5% scanned).

  2. @eg

    Спасибо.

    Хм. Вообще, не стоит относиться к JSLint как к истине в последней инстанции. Прежде всего этот инструмент отражает взгляд на то, как выглядит хороший код (с применением различных best practicies) с точки зрения одного человека – Douglas’a Crockford’a. И надо понимать, что и он может ошибаться. Во вторых, JSLiте подчеркивает как проблемные те места, на которых могут споткнуться минификаторы/обфускаторы вроде JSMin, YUI Compressor и т.п. Лично я воспринимаю вывод этого инструмента как рекомендацию, а не как непосредственное руководство к действию.

    Что касатеся того, на что я обращаю внимание, лучшим ответом будет скинуть мой профиль настроек JSLint в textmate JavaScript Tools bundle. Вот он: http://ulizko.com/wp-content/uploads/2008/12/jslint_preferences.txt

  3. Привет очень хорошая статья но пример скачать не возможно код примера сам код примера на странице не отображается отображается только комментарии.

Оставить комментарий

Последние твиты