Во время верстки сайтов часто приходится иметь дело с большим количеством 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-файлов.
Вот пример скрипта для скачивания.