Серверный процессинг LESS файлов «на лету» своими руками из песочницы

LESS — это популярный препроцессор для языка CSS, добавляющий возможности использовать константы, наследование, вложенные стили и много другое, чего так не хватает в CSS. Как только я познакомился с LESS я понял что это то, что мне нужно. Единственное, что омрачило мою радость — разработчики предлагают всего два варианта его использования: встраивать JavaScript файл, который занимается препроцессингом прямо в браузере или использовать специальный скрипт (который должен исполняться на node.js) который процессирует LESS файлы.

Вариант c процессингом LESS файлов на клиенте мне не понравился тем, что для больших LESS файлов это вызывает ощутимые паузы при загрузке страницы. Если использовать LESS версию твиттерного bootstrap-a — загрузка увеличивается на несколько секунд, что абсолютно недопустимо.
Вариант с предварительной компиляцией меня не устраивал тем, что приходится «вручуную» запускать препроцессор. Я видел программу, которая автоматически перегенерирует LESS файлы при их изменении, но она оказалась платной и только под МакОСь.

Мне же хотелось, чтобы LESS файлы процессировались на лету по запросу веб-сервером и, следовательно, подключались также, как и css. Такой подход лишен всех недостатков описанных выше. Однако, в этом случае чуть сложнее наблюдать за ошибками в синтаксисе LESS файлов: их можно будет видеть только в логах процессирующего сервера. Однако ошибки именно в синтаксисе LESS файлов у меня случались крайне редко, так что это не стало проблемой.


Решение


К своему сожалению, я не нашел процессоров LESS файлов ни на Python, ни на Java (языки, на которых я разрабатываю). Поэтому я просто написал маленький http-сервер на node.js, котрый использует код «официального» процессора LESS файлов и процессирует файлы «на лету». При таком подходе, правда, приходится перенаправлять раздачу less файлов на отдельный сервер, а значит без nginx не обойтись. В принципе, можно использовать и другие сервера (например lighttpd), но nginx мне ближе и далее все примеры конфигураций я буду рассматривать для него.

За основу я взял скрипт lessc — консольный процессор LESS файлов, незначительно его модифицировав и сохранив все параметры запуска (позволяющие, например, сжимать генерируемый CSS). Исходный код написанного мной сервера. Он стартует на порту 1337 и любой запрос трактует как относительный путь до .less файла, который процессирует и отдает.

Далее настроил nginx так, чтобы все less файлы проходили процессинг на полученном http-сервере:

location ~ \.less$ {
      proxy_path http://127.0.0.1:1337;
}


Все! Тепепрь можно спокойно использовать LESS в своих проектах, не боясь за время загрузки и не вызывая препроцессор вручную. Просто вставляю .less файлы как обычные .css:
<link rel="stylesheet" type="text/css" href="bootstrap.less">


Кеширование

Процессировать LESS файл на каждый запрос на боевом сервере, безусловно, слишком накладно. Тут нас спасет возможность nginx кешировать ответы стоящих «за ним» серверов: в этом случае результат обработки LESS файлов будет кешироваться nginx-ом (на файловой системе) в течении заданного периуда времени, например 30 секунд. Один раз в 30 секунд процессировать LESS файлы не нагрузит систему, а 30 секундные задержки при обновлении файла со стилями обычно вполне допустимы (при желании можно выбрать и меньшее время хранения кеша).

Конфигурация nginx изменится следующим образом:
# Где хранить кеш, параметры кеширования + название конфигурации кеша
# Подробно: http://nginx.org/ru/docs/http/ngx_http_proxy_module.html#proxy_cache_path
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=microcache:5m max_size=1000m;
server {
     # ... Тут ваша конфигурация сервера
     # 
     location ~ \.less$ { # фильтруем файлы с расширением .less
          proxy_cache microcache; # активируем кеширование (на основе конфигурации microcache) только для раздачи .less файлов
          proxy_cache_valid 200 30s; # включаем кеширование ответов сервера с кодом 200 (ОК) в течении 30 секунд
          proxy_pass http://127.0.0.1:1337; # перенаправляем запрос на http-серер занимающийся процессингом
     }
}

Пошаговая инструкция


Рассмотрим теперь кратко, по шагам, что нужно сделать, что бы развернуть такое у себя:
  1. Устанавливаем node.js. На линуксе — ставим из репозиториев, на мак и вин ставим с оффсайта
  2. Устанавливаем (если еще не используется) nginx и конфигурируем на раздачу вашего сайта
  3. Скачиваем c github исходники LESS процессора
  4. Берем исходный код http-сервера и кладем его в скаченную папку с LESS процессором в поддерикторию bin
  5. Находясь в директории bin в исходниках LESS запускаем http-сервер командой
    node lessserv --path=/path/to/project/root
    

    После --path= идет путь до корня вашего проекта, откуда будут раздаваться .less файлы. Все ошибки в процессировании будут выводиться в консоль.
  6. Перенаправляем все запросы к .less файлам через запущенный http-cервер
    location ~ \.less$ {
          proxy_path http://127.0.0.1:1337;
    }
    

  7. На боевом сервере включаем кеширование для результатов обработки .less файлов

Ссылки


+25
13 февраля 2012, 16:19
114
meta4 13,1

комментарии (46)

+5
biophreak #
А почему бы не генерить 1 раз из less файлов обычные css-ки? И подгружать, соответственно их.
–1
meta4 #
В случаи разработки less файлы я постоянно правлю — по этому постоянно перегенерировать их неудобно. Да и для продакшена — зачем усложнять себе выкладку дополнительным шагом сборки, которого легко можно избеждать.
+5
Ganesh #
Ну так и разрабатывайте с динамической компиляцией в браузере, а на продакшене компилируйте при сборке приложения.
0
meta4 #
>Ну так и разрабатывайте с динамической компиляцией в браузере, а на продакшене компилируйте при сборке приложения.

Это значит что нужно в продакшене и в девелопменте эти части страницы будут различаться. К чему такие сложности если можно без этого обойтись?
+5
Ganesh #
А на кой черт на продакшене использовать не скомпилированный код? Может вы еще и на продакшене в режиме онлайн минифицируете и обфусцируете JS код? Мне почему-то больше кажется, что сложности написаны именно у вас.
–1
Holy_Cheater #
Да, я так и делаю. В cms, которую пользую на работе, есть фунционал который за меня минифицирует js/css. Я об этом даже не задумываюсь и не вспоминаю при правках. В чем сложность для меня-то?
0
Ganesh #
Я конечно, могу процитировать весь топик, но мы же взрослые люди и умеем скролить.
У меня такое впечатление, что вы просто не знакомы с понятием сборки приложения (например maven, ant), и у вас возникает какие-то трудности в понимании, того что для решения проблемы достаточно добавить в сборку для продакшена этап компиляции css (в данном случае сборщик у вас наверное тот самый функционал). А уж простите править файлы на продакшене — это либо форс мажор, либо отсутствие мозга.
+1
mrjj #
лол, они почти всегда будут различаться и для этих случаев очень неплохо подходят штуки вроде fabric, куда полезнее будет ман по управлению ноды из fab, свежо и удобно.
0
Coder #
… видел программу, которая автоматически перегенерирует LESS файлы при их изменении, но она оказалась платной и только под МакОСь.

LESS.app, winless, less,...?
0
Coder #
Но да, не уверен про автоматическую компиляцию при изменении.
+1
meta4 #
Ага, они. Мне подход с трансформацией на лету больше нравится — получается, как будто less поддерживается самим браузером, мне не надо думать о том у куда и во что генерируются эти самые .less файлы.
0
cloud #
incron — автомтическое что угодно при изменении чего угодно
habrahabr.ru/blogs/linux/66569/ — статья на хабре про это
+1
mrjj #
Хм, у меня это 10 строчек кода маленького демона делают, а люди за это деньги берут ?!
0
shaelf #
Люди берут за удобный интерфейс. Вообще идея УГ. Могу посоветовать почитать про мавен или ант:)
+1
rmaksim #
while true; do inotifywait -e modify css.less; lessc css.less > css.css; done;
0
kroks #
Что если хранить в начале less файла строку хеша от файла (всех остальных строк), и если файл изменился, то только тогда заново генерировать?
0
meta4 #
Зачем? Изменение даты последнего имезенения файла в большинстве случаев должно хватать.
+1
Makito #
Недавно на хабре были статьи про Less, приводилась прога SimpLESS.
Бесплатна, работает на всех платформах, на лету отслеживает изменения и компилит их в css причем еще и минимизирует его.
Сейчас использую данную прогу на нескольких проектах, очень удобно, а на продакшн выкладываю чистый css
–1
meta4 #
>Бесплатна, работает на всех платформах, на лету отслеживает изменения и компилит их в css причем еще и минимизирует его.

На счет минимизации — так это LESS штатно умеет, и мой сервер тоже. Но с моим подходом вообще не надо задумываться на чем пишешь — на less или css.
0
Timosha #
incron + препроцессор less, и не придётся «запускать ручками» :) много проще чем сервер + прокси
0
meta4 #
>сервер + прокси

Так nginx я в любом случаи использую (и многие используют). Добавить пару строчек в нгинкс и запстуить одной командой сервер не сложнее чем добавить задачу в крон, а крон выполять надо будет очень часто — я быстро правлю+ переключась в браузер :)
0
Timosha #
incron — это cron который срабатывает не по времени, а по событию, например, после модификации файла :)
0
meta4 #
Спасибо, не знал про его существование.
+2
maxx_es #
я сделал (не)множко по-другому:

etc/nginx/nginx.conf
perl_modules etc/nginx/perl;
perl_require lesscss.pm;


etc/nginx/perl/lesscss.pm
package lesscss;
use File::stat;
use nginx;

sub handler
{
	my $r = shift;
	
	$r->send_http_header("text/css");
	return OK if $r->header_only;
	
	if(-f $r->filename)
	{
		$less = $r->filename;
		$less =~ s/\"//g;
		$css = $less;
		$css =~ s/\.less$/.css/gi;
		
		if(!stat($css) || stat($less)->mtime > stat($css)->mtime)
		{
			system("PATH=/opt/local/bin lessc \"".$r->filename."\" > \"".$css."\"");
		}
		if(-e $css)
		{
			$r->sendfile($css);
			$r->rflush;
		}
	}
	
	return OK;
}

1;
__END__


etc/nginx/vhost/???
location ~* ^.+\.less$ { perl lesscss::handler; }


в итоге nginx сам смотрит за изменениями в .less-файле, и незаметно обновляет .css, лежащий тут же рядом.
решение не идеально, но для домашнего сервера пойдёт.
–1
kratkar #
Спасибо. Ваше решение самое то!
+4
cadmi #
Не делайте так. В nginx Perl не для этого. На тестовом сервере, куда никто кроме Вас не ходит, еще покатит. Но очумелые ручки же это «самое то» решение непременно на боевой выкатят.
0
Sannis #
Расскажите, в чём подвох с Перлом?
0
cadmi #
Во время выполнения perl внутри nginx, скриптом напрочь блокируется весь worker. Perl, встроенный в nginx — это совсем не то же самое, что mod_perl в Apache. Он не предназначен для генерации страниц, лазания в SQL, форкания процессов (как в вышеприведенном примере с вызовом system() и less) и т.п.

Встроен для удобства написания сложной логики обработки location или написания хитровывернутых rewrite теми, кому это действительно нужно. Чтобы не сковывать их ограничениями синтаксиса nginx.conf.

Ну максимум — что-то собрать из SSI.
0
Sannis #
Спасибо :)

P.S. Хотя мне недавно попадалась статья на тему прикручивания libdrizzle к Nginx.
0
cadmi #
Олег в рассылке (и, кстати, документации тоже) явным образом и черным по белому НЕ рекомендует сетевые обращения из встроенного Perl. Настойчиво предлагается «ограничиться операциями, время исполнения которых короткое и предсказуемое, например, обращение к локальной файловой системе».
0
Sannis #
У нас ограничивается обращением по сокету к локальному MySQL с таблицей в памяти. Но думаю над тем, когда же это станет узким местом.
+1
cadmi #
Если человек знает, что делает и понимает, чем это грозит, то ничего страшного.
0
cadmi #
Тьфу, блин. Игорь, конечно. Вредно в два окна сразу писать.
+3
AlexoLive #
Я себе hook сделал в Mercurial, и ничего лишнего)
–1
meta4 #
Эээ. Это немного другое — не будете же во время разработки при изменении свойства «напосмотреть» коммититься.
+1
AlexoLive #
На посмотреть и js скрипт сойдет
0
student_ivan #
Поделюсь своим решением github.com/studentIvan/Perl-LESS-Watcher
0
cramen #
я в php проектах использую leafo.net/lessphp/
rewrite правила к css проверяют, есть ли less файл с аналогичным именем и если есть и дата модификации его позже, чем у css, то компилирую less в css
дальше вне зависимости от первого условия отдается содержимое css или ошибка 404, если его нету.

для низко нагруженных проектов более, чем достаточно и довольно быстро.
0
shternberg #
Как мне кажется, гораздо разумней использовать для данных целей assetic – он позволяет налету компилировать LESS, SASS, Compas, сжимать css, javascript и многое другое.
Позволяет генерировать скрипты, как на лету, так и делать дамп на диск.

github.com/kriswallsmith/assetic
0
mac2000 #
Поскольку результаты кешируються то можно было бы просто в кроне запускать компилятор less, а на сайте уже дергать нагенеренный CSS.
+1
tonic #
Такое решение из серии вредных советов. На боевом сервере нужно собирать css в момент деплоя. Деплой же всё равно должен быть автоматизирован так или иначе, какая разница что будет на одну операцию дольше.
А для отладки, локально, используйте все что угодно.
0
meta4 #
>Такое решение из серии вредных советов. На боевом сервере нужно собирать css в момент деплоя.

Так вы напишите, чем плохо то. Зачем вообще собирать, а не на лету обрабатывать, с учтом того что кеширование все равно решает проблему производительности? Тот же python/php за то и любят, что его файлы собирать не надо. Nginx gzip-ает тоже налету. Почему же именно в этом случае должна быть сборка?
0
VBart #
«Nginx gzip-ает тоже налету»
Расходуя процессорные ресурсы на каждый запрос, увеличивая latency. Кстати, nginx.org собирается make-файлом, генерация и сжатие всех страниц выполняется на этапе сборки.

Зачем расходовать ресурсы впустую?

«Тот же python/php за то и любят, что его файлы собирать не надо»
Не за это python любят. В сущности это, как раз, пустяк.
+1
achekalin #
Вообще для боевого деплоя есть make, а для тестового сервера — можно делать как угодно, да. Но тогда и less на клиенте не должен раздражать, он даже удобнее.
0
biophreak #
Действительно, не проще будет написать Makefile, который будет делать все сам?
Я не только про конвертацию less в css, можете туда еще например проверку на ошибки в языке (для перла я юзаю Perl::Critic например, для ноды jslint), тесты в конце концов прикрутить.
Подготовку минифицированных js, гзип, объединение чего надо в 1 файл и так далее.

Например потом можно в .deb все упаковать и положить в репозиторий.
В бою-же просто apt-get update && apt-get install package.
0
Tenphi #
github.com/Tenphi/node-jss

вдруг интересно будет)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.