Организация, оптимизация и кеширование CSS и JS файлов

Организация, оптимизация и кеширование CSS и JS файлов

Всем привет!

В последний год я участвовал в разработке нескольких достаточно крупных (с точки зрения верстки и клиентских скриптов) проектов и сейчас я хочу поделить с вами своим опытом и программными наработками.

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

Исходные задачи

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

Потом я понял, что когда работаешь над крупным проектом, достаточно сложно (мне по крайней мере) держать весь код или стили в 2-3 файлах. Когда количество строк переваливает за 2000-3000, найти что-то там или дописать в нужное место становится нереальным. Плюс увеличивается вес файлов, которые загружает клиент и сайт начинает работать медленнее.

Итак, задачи, которые я хотел решить:

  1. организовать файлы так, чтобы грузить клиенту только нужные ему в данный момент;
  2. файлы должны быть логично и удобно разложены на сервере; при этом сторонний разработчик должен быстро понять что и как здесь устроено;
  3. отдавать клиенту сжатые файлы (без комментариев и лишних символов);
  4. минимизировать количество отдаваемых клиенту файл, в идеале — по одному на стили и скрипты;
  5. сделать так, чтобы клиент не загружал файлы каждый раз заново (кеширование);
  6. при обновлении какого-то файла автомически должны обновляться и загружаться заново измененные файлы;
  7. процесс сжатия и кеширования должен быть максимально автоматизирован.

Решение организации файлов

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

Для скриптов я сделал еще одно разделение. Файлы с функциями у меня отдельно, а вызов этих функций — в другом файле. То есть если для раздела блогов функции хранятся в файле blogs.js, то их вызов делается в init.blogs.js. Так, зайдя в init.blogs.js сразу видно, какие именно функции запускаются при загрузке раздела.

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

Чтобы стало немного понятнее, приведу пример файловой структуры одного из проектов:

Пример организации CSS и JS

Подключение JS файлов в HTML делается в таком порядке: сначала в нужном порядке идут файлы, содержащие описания функций, а потом — файлы, делающие вызов функций:

<script type="text/javascript" src="/js/common.js"></script>
<script type="text/javascript" src="/js/blogs.js"></script>
<script type="text/javascript" src="/js/init.js"></script>
<script type="text/javascript" src="/js/init.blogs.js"></script>

Решение сжатия и кеширования файлов

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

То есть если обычное подключение файлов скриптов выглядит вот так:

<script type="text/javascript" src="/js/common.js"></script>
<script type="text/javascript" src="/js/blogs.js"></script>
<script type="text/javascript" src="/js/init.js"></script>
<script type="text/javascript" src="/js/init.blogs.js"></script>

То подключение через скрипт выглядит вот так:

<script type="text/javascript" src="/compress.php?js,js/common,js/blogs,js/init,js/init.blogs"></script>

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

Для решения первой задачи я решил использовать PHP библиотеки CSSmin и JSmin, соответственно. Для решения второго вопроса я сделал так, что скрипт сначала проверяет, есть ли сжатая версия этого набора файлов, и если есть, то отдает ее, а если нет, создает ее заново. В итоге я получил скрипт с такими возможностями:

  • сжатие CSS и JS на с использованием CSSmin и JSmin, соответственно;
  • объединение и сохранение сжатых версий файлов;
  • автомическое обновление сжатой версии файлов, если какой-то из них был изменен;
  • заголовки кеширования для браузеров.

Практическое применение

Сейчас, уже на третьем проекте, я делаю так. На тестовых макетах я подключаю все файлы отдельно, без использования скрипта сжатия. Так легче делать отладку, так как FireBug покажет, в каком файле и строчке произошла ошибка.

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

Как использовать

Скрипты для работы нужны библиотеки CSSmin и JSmin. Так же нужно указать папку, где хранить сжатые версии файлов. У меня это /css/compressed/ и /js/compressed/, соответственно.

Файлы, которые необходимо сжать, нужно указывать следующим образом. Сначала указывается тип (css или js), потом через запятую путь до файл (только имя, без расширения) относительно файла-компрессора. В моем случае при условии, что compress.php и папки css и js находятся в одной директории, подключение выглядит как-то так:

<script type="text/javascript" src="/compress.php?js,js/common,js/blogs,js/init,js/init.blogs"></script>

Итак, ниже есть архив с файлом compress.php и необходимыми библиотеками:

Скачать (PHP)

Буду рад выслушать ваше мнение, вопросы и предложения в комментариях. Кстати, если кто-то сможет сделать такой же скрипт на других языках (.NET, Ruby, etc.), буду очень благодарен.

Расскажите друзьям

Оцените статью:
  • 1
  • 2
  • 3
  • 4
  • 5

Комментарии — 36

Сергей
Спасибо, думаю будет полезно.
#
Сергей
d1mmmk
жаль нет примера, хотелось бы посмотреть что будет в итоге не запуская скрипт. а если конкретнее, то как сжимается css? если в одну строчку то подозреваю что будут проблемы с гязными хакками для ie7 типа
//margin:20px 0 0;
Алексей
Спасибо за статью.
Один вопрос: как Ваш скрип определяет был ли изменен файл?
#
Алексей
Алексей
*скрипт
#
Алексей
Grin
d1mmmk, да, в одну строку. Но если хотите, всегда можно отключить это, убрав обработку через CSSmin.
Или используйте условные комментарии для IE вместо хаков.

Алексей, по времени изменения файла.
Evgenij
Недавно открыл для себя замечательный инструмент для VisualStudio 2010. Называется Chirpy.
Он позволяет сжимать, объединять CSS и JS файлы прямо во время разработки. Доступны разные инструменты сжатия css и js (Yahoo Compress, Google Closure Compiler, Microsoft AJAX и еще че-то).

А самое на мой взгял крутое - это возможность использовать LESS синтаксис в CSS. Про это у нас уже была статья.
Тормоз
Интересно, а насколько это всё оправданно? В заметке почему-то совсем не рассказано, какие именно преимущества дают все эти заморочки. То есть в цифрах: было так-то, стало так-то, типа прогресс.
Grin
Тормоз, плюсы в скорости загрузки самой страницы. Это все можно довольно просто увидеть на своем сайте, если в фаербаге посмотреть объем статики (css / js) и скорость ее полной загрузки до и после применения скрипта.

Ясно, что если из файла вырезать комментарии и пробелы, то весить он станет заметно меньше. А если отдавать клиенты не 20 файлов, а 2, то загрузятся они так же быстрее.

Я как-то провел такое исследование на одном проекте, но цифр сейчас не осталось.
TIgor
В сжатии скриптов и цсс есть смысл. Я себе уже давно поставил minify и провел некоторые тесты:
http://tigor.org.ua/wp-minify/
Тормоз
Ну вот, а почему форма меня не помнит? (

Про количество файлов тут очевидно, конечно, ускорение должно быть и значительное. Я к тому, что неужели на самом деле удобней вот так в разных CSS всё редактировать? Откуда там столько кода набирается для разделения? Возможно, изначально непродумана архитектура оформления.
Grin
Странно, меня она помнит ;-)

Да, набирается обычно из-за того, что в различных разделах сайта есть элементы, которые есть только там, и смысла отдавать это туда, где это не нужно, нет.
Чистяков Денис
Спасибо за статью, оказывается CSSmin полностью переписали, замечательная новость.
Я бы порекомендовал еще добавить номер версии текущего «минифицированного» файла, и сохранять его физически (на файловую систему или в мемкеш, или не важно куда )), а генерировать если запрашиваемый файл не существует. Что бы после генерации отдавать только средствами веб сервера. А например в случае nginx'а еще и делать .gz версию и поключить замечательный модуль «ngx_http_gzip_static_module» который при наличии подготовленного заархивированного файла автоматом отдает его. И да он еще и проверяет, а может ли клиент GZIP )
Т.е. запрашвать например: /compress.php?js,js/common,js/blogs,js/init,js/init.blogs -> /js/xxxx/common__blogs__init__init_blogs.js, а на файловой системе соответсвенно будут: /js/xxxx/common__blogs__init__init_blogs.js и /js/xxxx/common__blogs__init__init_blogs.js.gz, где xxxx номер текущей версии, это позволит:
1) отдавать максимально долгоживущие заголовки
2) единовременно принудительно обновлять кеш пользователя при обновлении версии.
#
Чистяков Денис
Grin
Денис, спасибо за идеи!

Сейчас скрипт сохраняет сжатые версии физически. А GZIP у меня обычно сервер делает для все, что возможно.
Чистяков Денис
@Grin
Сделав предварительное сжатие вы не грузите этим в будущем процессор ;)
#
Чистяков Денис
Чистяков Денис
Как же похорошел CSSmin, и конечные автоматы вместо регулярок и CSS3 свойства и отличное конфигурирование, спасибо что заставили посмоетрть на него снова )
JSmin конечно староват лучше Google Closure Compiler, если хостинг позволяет выполня Java.
#
Чистяков Денис
Ilya
Что то не то, я подключаю все

Все сжимает, стиль схоронятся и доступен, но сам сайт почему-то не отображает стили...
Я что-то упустил?
#
Ilya
Николай
При работе над последним проектом встала та же самая проблема, но т.к. я совсем не программист, то пришлось её решать немного по-другому.
1. Количество CSS-файлов у меня должно быть строго меньше 32, т.к. IE игнорирует большее количество.
2. Для сборки файлов я использовал Ant. Сильно помогла статья Сергея Чикуёнка.
3. Проблему с кэшированием я своими силами не решил, придется просить помощи разработчиков.
#
Николай  
MaxSof
О нормальная темка) Прикручу к своему сайту. Может быстрее заработает)
qua12345
Очень интересная статья! Спасибо за полезную информацию!))
Сергей
Интересная идея возьму на заметку, что то не когда даже на задумывался, надо обязательно попробовать.
niquola
Dojo & Google Compiler делают это и немного больше
#
niquola  
Легенда
Всегда пользовался http://code.google.com/p/minify/ и всегда помогало!
Doctor Zlo
Интересно читать, спасибо!
prokopa
было интересно узнать чтото новенькое
Soshi
Спасибо за статью. Скрипт очень пригодился.
Только у меня вопрос. Как подключить сss?
#
Soshi
Саня
А с какой программы скриншоты? красивое оформление
#
Саня
Grin
Саня, это Coda по Мак
bemate
Предварительное сжимание в .gz, о чём говорил @Чистяков Денис, использую в своей CMS - результат отличный!
Причём не только для минифицированных скриптов и стилей, но и для самого сгенерированного html документа.
Это имеет смысл делать даже на высокопосещаемых сайтах с частой сменой контента. Проверено
#
bemate  
Илья Баранов
В сжатии скриптов и цсс есть смысл. Я себе уже давно поставил minify и провел некоторые тесты:
http://tigor.org.ua/wp-minify/
Дмитрий
Использовал данный скрипт для сжатия YUI библиотеки, резуальтат отличный! Но вот при ПЕРОМ создании минизированного файла как js так и css заметна значительная задержка (видимо здесь работает compress.php). Не подскажете что можно использовать чтоб максимально ускорить процесс?
#
Дмитрий
Виктор
Интересная и полезная статья,спасибо!
Владимир
Очень интересный материал - будем пробывать ,спасибо!
Федор
Очень интересная статья. Я попробовал подключить скрипт к своему сайту, но получилось следующее: создался сжатый файл с внутренностями указанных скриптов, но сайт не может работать с этим новым скриптом. Я пробовал прописать вручную имя нового скрипта, но происходит тоже самое. Скажите пожалуйста, после загрузки страницы что должно быть вместо . Если эта строка должна заменяться на строку с соурсом на новый скрипт, то у меня это не происходит.
#
Федор
Alex
решил скорость потестить, что то не заработало :) говорит нет такого файла "/path/file.css", хотя он есть,вероятно с правами что-то, разбираться не стал... :)
о такой системе начал думать года 3 назад. реализовал около года назад :) тоже использую minify, но у него есть недостаток: сам он как-то "криво" кеширует файлы -- то только какую-то часть закеширует, то вообще не хочет обновлять кеш после перезаливки файлов.
обошел так: перед выдачей склеиваю "ИМЯ_ФАЙЛА+РАЗМЕР", получаю md5-хеш, затем смотрю, есть ли такой файл? если нету, склеиваю все файлы, задаю 32-х символьное имя и отдаю его minify -- точно съест )

еще есть момент - фоновые изображения (background:url(...)), которые естественно находятся на сервере -- часто они попадают в кеш юзера и даже после обновления не видны (особенно касается спрайтов) -- тут тоже надо что-то думать :)
я делаю так: прохожу регекспами по тексту, надожу изображения на сервере и ставлю им дату изменения в UNIX-формате, типа этого: "img.gif?1294900905". после изменения CSS и изображения, адрес меняется и оно перезагружается...
а кто как еще обходит это? :)
#
Alex
Psylone
Благодарю автора за статью.

Под Ruby есть замечательная система Sprockets: https://github.com/sstephenson/sprockets

Новый комментарий

как выглядит какой тег
жирный текст <b>жирный текст</b>
курсивный тект <i>курсивный тект</i>
зачеркнутый текст <s>зачеркнутый текст</s>
подчеркнутый текст <u>подчеркнутый текст</u>
ссылка <a href="адрес">ссылка</a>
function foo() { ... }
<pre><code>function foo() { ... } </code></pre>
разрешенные теги или посмотреть как будет выглядеть
как выглядит какой тег
жирный текст <b>жирный текст</b>
курсивный тект <i>курсивный тект</i>
зачеркнутый текст <s>зачеркнутый текст</s>
подчеркнутый текст <u>подчеркнутый текст</u>
ссылка <a href="адрес">ссылка</a>
function foo() { ... }
<pre><code>function foo() { ... } </code></pre>
разрешенные теги или посмотреть как будет выглядеть

metin2 pvp metin2 pvp serverler pvp serverler