Сжатие CSS файлов
Иван Никитин
Во время верстки сайтов часто приходится иметь дело с большим количеством CSS файлов, например, отдельные стили для описания цветов, фонов элементов, отдельные стили для мобильных устройств и т.п. Зачастую количество файлов .css бывает хорошо за десяток. Однако для оптимизации производительности сайта настойчиво рекомендуется минимизировать количество загружаемых файлов и по возможности включить их сжатие. Как это сделать достаточно просто рассказывает эта заметка.
Поскольку мне приходится делать это буквально на каждом сайте, я озадачился написать достаточно простое и легко переносимое решение, и, наконец, сегодня до этого дошли руки.
Итак, идея довольно простая:![]()
- Сваливаем все css файлы в одну папку на сайте, например, /css/
- C помощью скрипта, который стоит дефолтовым документом в этой папке, проверяем время изменения файлов
- На основе этого времени реализуем правильную реакцию на запрос GET If-Modified-Since, чтобы задействовать кэш браузера и возможных прокси
- Обязательно разрешаем кэширование, при условии валидации в запросе (см. пункт выше)
- Сливаем все файлы в CSS в один поток и сжимаем его gzip
- Что получилось, то и отдаем пользователю (304 Not Modified или gzip поток)
В общем, эта нехитрая идея реализуется вот таким скриптом index.php
<?php /* * Сборка CSS файлов в один и передача его с сжатом виде клиенту */ // Расширение файлов, с которым работаем define('FILE_EXT', '.css'); // Тип содержимого define('CONTENT_TYPE', 'text/css'); // Политика кэширования define('CACHE_CONTROL', 'public, max-age=86400, must-revalidate'); // Ищем все файлы по расширению, стоем спискок файлов, проверяем дату последней модификации $myFiles = array(); $lastModified = 0; if ($handle = opendir('.')) { while (false !== ($file = readdir($handle))) { // Это мой файл? if (strpos($file, FILE_EXT) !== false) { // Запомним файл $myFiles[] = $file; // Дата его модификации $currentFileModification = filemtime($file); if ($currentFileModification > $lastModified) $lastModified = $currentFileModification; } } closedir($handle); } // Если файлов нет, возвращаем 404 if (count($myFiles) == 0) { header('HTTP/1.0 404 Not Found'); exit; } // Если был запрос If-Modified-Since, обрабатываем его if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Разбираем заголовок If-Modified-Since и формируем timestamp $if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'])); if ($if_modified_since > $lastModified) { // Изменений не было header('HTTP/1.1 304 Not Modified'); header('Cache-Control: ' . CACHE_CONTROL); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); exit; } } // Передаем заголовки header('Content-Type: ' . CONTENT_TYPE); header('Cache-Control: ' . CACHE_CONTROL); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); // Старт сжатия ob_start("ob_gzhandler"); // Порядок передачи -- по алфавиту по именам файлов asort($myFiles); // Передаем файлы foreach($myFiles as $file) include($file); // Вывод результата ob_end_flush(); ?>
Теперь наши CSS подключаются невероятно просто:
<link rel="stylesheet" type="text/css" href="/css/">
Результат, как видно, налицо!
- Более чем 3х кратное сжатие (7,37 Kb сжимается до 2 Kb)
- Активное использование кэша браузера
- Минимальное число обращений к серверу
Первый запрос:
Второй запрос (другая страница):
Этот простой способ можно также применить и для сжатия нескольких JS-файлов.
Вот пример скрипта для скачивания.
Сжатие файлов проще делать настройками веб-сервера. Чтобы быстрей отдавать статику лучше использовать NGINX как проксирующий сервер.
Про сливание CSS и JS файлов:
1. Браузер не должен запрашивать файлы ему не предназначенные, хотя, он это делает. Но если слить screen и handheld css в один файл? Там же буде пересечение селекторов.
2. Аналогично и с JS. Если у вас в файле присутствует код, который работает с DOM не всего сайта, а только с некоторыми страницами? Будет ошибка.
Еще один минус данного подхода, это то, что каждый раз при запросе статики приходится дергать PHP.
В общем, я пришел к такой стратегии: NGINX в качестве проксирущего сервера для статики; вся статика на поддоменах, чтобы не утыкаться в 4-+ параллельных запроса и правильные настройки кэширования.
Некоторые, особо продвинутые товарищи пишут скрипты билда, которые при обновлении проекта сами склеивают нужные файлы.
Ну и еще классная, но сложная в поддержки штука – css-спрайты.
Здравствуйте Иван. По этой ссылке http://rutracker.org/forum/viewtopic.php?t=3469109 раздаётся ваш видео курс (мне это неприятно ввиду того, что тренинг ваш слишком хорош для такой халявы)
P.S. (мыло в данных комента указано левое)
Абсолютно согласен, Алексей!
Предлагаемое здесь решение из разряда «дешево и сердито», для серьезных и больших проектов на production server конечно же сжатие лучше делать руками, и даже не средствами сервера, а просто сжав сами файлы.
По поводу пересечений селекторов CSS и DOM/JS — думаю, здесь все зависит от того, насколько корректно само приложение спроектировано, так ведь?
Согласен, но часто приходиться работать с чужим кодом.
Может проще сжимать в gz и кэшировать на стороне клиента?
Конечно же, проще! Но для быстрого действа, особенно, когда файлы часто меняются, предложенный способ хорош. А кэширование на клиенте как раз здесь и включается.