CSS и JS в одном файле
Раз уж я затронул тему снижения числа коннектов к серверу при загрузке сайта, то было бы уместо вспомнить незаслуженно забытую технику — объединение JS и CSS в одном файле.
Насколько я знаю, первым эту технику исследовал некий ShivaP в статье «Combine CSS with JS and make it into a single download!» ещё четыре года назад.
Идея такова: в файле специальными ухищрениями помещается и код JavaScript, и код CSS, файл подключается два раза — один раз через тег SCRIPT, второй раз через тег LINK (как CSS), браузер грузит файл один раз, а второй раз берёт его уже из кеша.
Автор проверил свою идею на двух браузерах — IE и FireFox, на том и остановился.
Я проверил эту идею на гораздо бо́льшем числе браузеров (обнаружил проблемы на некоторых «Хромах» и исправил), а так же немного её видоизменил, разрешив использовать многострочный CSS без особых неудобств. Последнее нужно персонально мне, так как у меня нет возможности прогонять свой CSS через оптимизаторы и вытягивать таким образом код CSS в одну строку, а я хочу использовать эту технику у себя на сайте.
У техники есть ограничение, которое не портит много крови, но о нём необходимо знать — в тексте CSS и JS не должна встречаться конструкция «*/». Если в CSS достаточно просто выкусить все комментарии («*/» там, можно считать, нигде больше встретиться не может), то в JS за этим придётся следить, так как такая комбинация вполне может встретиться, в частности, в регулярных выражениях. Например: var regexp = /smth*/g;
В таких случаях звёздочку необходимо прятать (например, заменять её в регулярных выражениях на \x2A или разбивать строку: «smth*» + «/»).
Как ShivaP, первый исследователь этой техники, решил проблему размещения JS и CSS в одном файле можно почитать в оринальной статье, я же поступил несколько иначе, но сходным образом. Конструкции размещаются так, чтобы JS оказывался в комментариях CSS и наоборот. Я похожими вещами уже занимался неоднократно.
Мой код выглядит следующим образом (я поставил рядом две картинки, чтобы можно было сравнить как один и тот же код воспринимается интерпретатором JavaScript и парсером CSS):

Как видите, принцип довольно несложен.
Теперь несколько слов о том как это всё подключается:
<link type="text/css" rel="stylesheet" href="core.jscss" />
<script type="text/javascript" src="core.jscss#2"></script>Обратите внимание на «#2» после core.jscss, этот хак позволяет обойти странное поведение некоторых версий «Хрома» — без него браузер не делал попыток подгрузить файл ещё раз и JavaScript не выполнялся.
Немаловажно так же, что заголовок
У себя на сайте я буду использовать скрипт наподобие нижеприведённого, чтобы разом разрешить все проблемы и выставить правильные заголовки. Формат вызова простой: скрипту через запятую перечисляются файлы, которые нужно объединить и подключить (файлы берутся из папки «media», см. переменную «$dir»): /util/js+css.php?core.js,core.css,another.css,another.jsЧитать дальше
<?
ob_start();
$dir = 'media/';
$expires = 10 * 365 * 24 * 60 * 60; // 10 years
$files = explode(',', str_replace('..', '', $_SERVER['QUERY_STRING']));
$css = $js = array();
foreach ($files as $file) {
switch (pathinfo($file, PATHINFO_EXTENSION)) {
case 'css':
$css[] = $dir . $file;
break;
case 'js':
$js[] = $dir . $file;
break;
}
}
$css = @array_map('file_get_contents', array_unique($css));
$js = @array_map('file_get_contents', array_unique($js));
$css = implode($css);
$js = implode($js, ';');
// removes /* comments */ from css
$css = preg_replace('!/\*.*?\*/!s', '', $css);
// removes only first /* comment */ from js
$js = preg_replace('!^/\*.*?\*/!s', '', $js);
echo <<<JSCSS
// /*
$js
"*/{"/*"}
$css
*/
JSCSS;
$len = ob_get_length();
$txt = ob_get_clean();
$etag = '"' . sha1($etag) . '"';
header("Content-type: */*; charset=utf-8");
header("Expires: " . gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + $expires));
header("Cache-control: max-age=$expires, public");
header("Content-length: " . $len);
header('ETag: ' . $etag);
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
list ($hash) = explode(';', $_SERVER['HTTP_IF_NONE_MATCH']);
if ($hash == $etag) {
header('HTTP/1.1 304 Not modified, thanx for yr question');
exit;
}
}
echo $txt;
Комментарии 35
Вы считаете это готовым к использованию в боевых проектах?
C одной стороны идея неплохая, на один коннект меньше, но с другой стороны — концептуально неверно смешивать оформление и код. Скрипты могут быть разными на разных страницах или не везде вообще нужны. А не проще все держать в отдельных файлах и подключать просто серверным инклюдом — тогда вообще
Он и не смешан. Оформление в CSS, код в JavaScript. То, что это приходит в одном файле к клиенту, концептуально ничего не меняет.
Если сайт состоит из одной страницы, так и надо делать, в остальных случаях так делать нельзя. Вынесенный в отдельный файл CSS и JS снижает весHTML-страницы. Когда клиент перейдёт на неё, ресурсы (JS и CSS) будут загружены из кеша.
2. На слабых компьютерах такой код существенно увеличит время полной загрузки страницы
3. Головная боль с поддрежкой и разработкой.
И еще, кажется у тебя на сайте была статья о подключении дополнительной .js из подключаемого .js файла. Выглядело как обычный вывод строки. Ведь ни что не мешает написать скрипт который оформит два document.write, и модно даже многострочники оформлять, заменяя перевод строки на /n.
Вроде всё верно написано, где опечатка?
Комментарий для http://orcinus.ru:
Я ничего про это сказать не могу, просто не знаю о таких случаях.
Производительность такого решения ниже.
Производительность ниже, так как скрипт сначала придётся разместить в DOM, потом проинтерпретировать. Кроме того, это начисто убивает возможность использования defer при необходимости.
«Содержимое, находящееся по указанному URL не является текстом: https://bolknote.ru/files/exp-jscss/js%2Bc … .css%C2%BB пишет.
На сайте bolknote.ru keepalive выключен. Он тут не мимо, поскольку ваша цель уменьшить число соединений. Суть KeepAlive как раз в том, чтобы делать несколько запросов в одном соединении.
Увы, за 12 лет существования HTTP/1.0 производители браузеров и
Комментарий дляmaxim-zotov:
Опечатка, должно быть HTTP/1.1
Комментарий дляmaxim-zotov:
Извините, наврал. Включён.
Комментарий дляmaxim-zotov:
Цель не уменьшить количество соединений, а уменьшить время загрузки — соединения тут лишь постольку поскольку их количество в браузере ограничено.
Ну да, ну да. Подмена отдачи статического файла вызовомphp-скрипта это вообще изощрённое убийство сервера.
Что до времени, вот честно скажу, 5+5 миллисекунд значительно меньше 35 миллисекунд.
$ http_ping https://bolknote.ru/files/exp-jscss/js%2Bc … js%2C1.css
153 bytes from https://bolknote.ru/files/exp-jscss/js%2Bc … js%2C1.css: 35.912 ms (2.01c/33.867r/0.035d)
$ http_ping https://bolknote.ru/pavatar.png
3939 bytes from https://bolknote.ru/pavatar.png: 5.874 ms (2.113c/3.525r/0.236d)
Отдаваемый файл вполне может быть сохранён на сервере и отдаваться статически. Концептуально, решение не заставляет генерировать его на каждый запрос.
Комментарий дляmaxim-zotov:
В методике нигде не написано, что есть такая необходимость. Ссылка, которую вы используете, описана как «страничка, где можно проверить как это работает под вашим браузером», а не «рабочее решение, которое используется у меня на блоге».
Прочитайте, я уверен, найдёте для себя массу полезного.
http://rmcreative.ru/blog/post/clio-comman …
Это можно видеть на странице с результататми поиска.
Не рискуйте, Евгений
На самом деле сss и js вшиты в код всех страниц выдачи яндекса..
Видимо SERP формируется допотопным скриптом.
Планируется смена архитектуры, а допиливать существующую никто не хочет.
Там ничего не делается просто так и никто ничего не забрасывает, это «градообразующий» сервис всего Яндекса, наш поиск.
Комментарий для Евгения Степанищева:
Возможно, Яндекс и непрозрачная компания, но не скрывает, что ее архитектура поисковой выдачи устарела.
anatolix.livejournal.com/52547.html
А свои выводы я смогу сделать, просмотрев исходный код страниц выдачи.
Или это .jscss? Как это расширение проставить в имени файла?