CSSO
Ребята из симферопольского «Яндекса» сказали новое слово в оптимизации CSS — CSSO. Оптимизатор интересен тем, что умеет минимизировать с изменением структуры — то есть удалять перекрываемые свойства, производить слияние блоков с одинаковыми селекторами и тому подобное.
Сергей Крыжановский, который занимается программированием CSSO, в своём блоге хорошо описал отличия CSSO от YUI Compressor, CSSTidy и им подобных.
Например, CSSO умеет вот такое:
/* до CSSO */
.a {
color: red;
border: 0;
}
.b {
color: red;
}
/* после CSSO */
.a{border:0}.a,.b{color:red}
Меня несказанно печалит, что оптимизатор написан на JavaScript, потому что хостинг с серверным JS найти крайне проблематично, но зато оптимизацию в действии можно попробовать прямо в браузере.
А я правильно понимаю, что такая оптимизация CSS, как в примере выше, несколько противоречит рекомендациям по повышению скорости рендеринга в браузерах (из-за того, что .a встречается два раза):
http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
Или браузеры при парсинге CSS догадываются объединять эти две группы правил для .a в одну?
Комментарий для written.ru:
Честно сказать, не представляю как может быть иначе.
Комментарий для written.ru:
Проигрышь в скорости рендеринга, если и будут, то доли микросекунды, а выигрыш благодаря уменьшению размера куда более заметен. Сетевые задержки могут составлять секунды, а процессор обработает этот случай куда быстрее, чем будет передаваться лишние байты.
Комментарий для Евгения Степанищева:
При написании CSSO изначально закладывался потенциал портирования на другие языки. JS там очень простой, магия [почти] не применяется. Фактически, для каждой функции примерял то, как она будет выглядеть на Java, C++, Python и т. д. Вроде проблем не будет. Когда релиз выйдет на 2.0.0 (сейчас всё-таки слишком много TODO), можно будет плавно портировать.
Комментарий для written.ru:
Такие потери — больше теория, чем практика.
А я всегда использовал перечисление, как здесь используется. Никаких тормозов не замечал. И вроде все браузеры на это адекватно смотрят.
Комментарий для skryzhanovsky:
Да, я смотрел, код правда простой.
Кстати, если ты сразу смотрел на другие языки, тогда можно было написать какой-нибудь простой интерпретатор инструкций (DSL), тогда инструкции можно было бы обновлять сразу на всех языках.
Комментарий для skryzhanovsky:
Можно использовать «skryzhanovsky.ya.ru» вместо имени, тогда сразу по OpenID будет.
Комментарий для Евгения Степанищева:
А у меня заработал OpenID на твоем блоге.
Комментарий для orcinus.ru:
Не работал раньше?
Комментарий для Евгения Степанищева:
А разверни мысль про DSL, а то я сейчас нафантазировал вплоть до первичной компиляции CSS в CSS-байткод. :)
Комментарий для Евгения Степанищева:
Да, я тебе по этому поводу писал. А тут, смотрю сегодня — заработал.
Мне сейчас кажется, что в режиме структурной оптимизации должны только выдаваться предупреждения, но код трогать не надо. Исходный код на основе выданных ворнингов должен реструктурировать сам верстальщик, если захочет. Иначе такой исходный CSS будет просто «плохим» кодом, не готовым для продакшена — непродуманным, избыточным, плохо струтурированным. Зачем интеллектуальную (не очень алгоритмизируемую) работу по структурированию отдавать оптимизатору?
А часто повторяемость намеренно добавляется для удобочитаемости. И я бы её просто оставлял в покое, не выкусывал — процент в хорошем коде совсем невелик и погоды не сделает. Но это умозрительное имхо, конечно. Надо просто попробовать на «хорошем» коде )) Может, кто-то пробовал уже?
Но однозначно тулза пригодится для «плохого»/чужого кода. Иногда с первого взгляда видно — каша! покоцать её не глядя.
Комментарий для skryzhanovsky.ya.ru:
Не, я не про это. Придумать язык, на котором можно было бы выражать конструкции оптимизации. Что-то в сторону БНФ, плюс некий язык для правил изменения кода.
Комментарий для boltai-shaltai:
Дело в том, что CSS’ы объёмом в 50..70Kb — уже не редкость. Там сотни селекторов и тысячи свойств. В итоге получим под сотню предупреждений и рекомендаций, а разработчик это будет руками разводить? Дык уже не разводят, потому с середины прошлого десятилетия минимизаторы и пошли толпой.
Удобочитаемость — результат минимизации не предназначен для чтения человеком. Это сродни компиляции.
Комментарий для Евгения Степанищева:
А, понятно. Я зачатки такого уже сделал, правда, пока лишь в запретительных целях («не удаляй свойство», «не меняй порядок»). Посмотрим, будет ли удобно и в каком виде такой почти-DSL в CSS внедрять.
Комментарий для skryzhanovsky.ya.ru:
Вы тоже? Не верю. Любой _нормальный_ кодер, которому к тому же не капают на мозги и не гонят на скорость, всё же стремится написать стройный код.
Нну.. вот и плохо, что так пишут. Разгребать такой код весьма противно. Я не хотел бы оставлять за собой такую пакость.
Комментарий для skryzhanovsky.ya.ru:
Не-не, ты другое говоришь. Ты сейчас про запретительные инструкции в CSS, а я про то, чтобы парсер и оптимизатор делать не на JS, а на DSL, а интерпретатор DSL уже делать на нужном языке.
Комментарий для boltai-shaltai:
Кажется, упускаете два распространённых случая:
Повторю: CSS в 50..70Kb. :) Разработчик может быть сколь угодно гениальным, но удержать в голове контекст такого объёма и постоянно в голове же оптимизировать... Ну, возможно, да. На это будет уходить половина рабочего времени.
Комментарий для Евгения Степанищева:
Теперь понятнее. :) Такой вариант рассматривал, но отклонил. Я хочу позже написать немножко записей о минимизации, там же опишу причины, по которым CSSO на JS и сделан так, как сделан. Сейчас бегло получится, потому плохо. Но поверь, причины решений есть. :)
Комментарий для skryzhanovsky.ya.ru:
Про генерируемый код согласен.
Про собираемый — не совсем понятно. Собираются же фрагменты, описывающие независимые элементы? Откуда там повторы, если классы разные?
Под «хорошим» кодом я имел в виду такой, который как раз не надо держать в голове целиком. Те же «независимые блоки» вполне снимают головную боль, ограничивая контекст.
Комментарий для boltai-shaltai:
Классы разные, свойства и значения свойств одинаковые. Упростим до немного детского примера: .myclass0 { color: red } .myclass1 { color: red }.
В худшем случае после сборки итогового CSS из запчастей мы получим простыню из такого перечисления классов.
Думается, работу по превращению такой простыни в .myclass0,.myclass1 { color: red } лучше переложить на плечи роботов.
Комментарий для skryzhanovsky.ya.ru:
Нда, я тут проклинил и говорил больше о тупом копипастерстве, о явной избыточности. А такая группировка как самоцель как-то вылетела из головы.
А она что, реально экономит объём? Селекторы с именами классов и иерархией бывают довольно объёмные, и их «размножение» при группировке по свойствам может раздуть объём. Я понимаю, что вы там всё взвешиваете, на то и оптимизация. Но на реальном-то коде что происходит, есть у вас оценки?
Комментарий для boltai-shaltai:
Очень зависит от того, какой CSS подаётся на вход. Тут как с архивированием: сжимать сжатое уже никак.
На больших грязных CSS выигрываем от 2% до 5% без особой магии. На мелких обычно меньше. Правда, следует учитывать, что пока не все варианты группировок добавлены, а shorthand’ы покрыты только margin/padding, потому можно оптимистично добавить ещё 1%..2%.
VPS легко найти, кроме того, сжимать можно и на локальной машине.
Комментарий для boltai-shaltai:
Решил проверить на своем сайте.
До оптимизации: 1884 байта
После оптимизации: 1543 байта.
Получилась экономия в 341 байт.
Если бы там использовалась оптимизация по font-family, то код сократился бы очень сильно.
Комментарий для fulc.ru:
Это другие деньги.
Очень неудобно. Я часто редактирую мелочи прямо на удалённой машине.
Комментарий для orcinus.ru:
А попробуйте прогнать через YUI Compressor, потом через CSSO.
Комментарий для orcinus.ru:
Добавил сегодня одну из схем группировок, теперь ваш CSS сжимается до 1308 байт. :)
На всякий случай отмечу: это не схема именно по font-family, просто в вашем случае именно повторы font-family удачно попали под новую группировку.
Комментарий для skryzhanovsky.ya.ru:
Смотри, я нашёл как улучшить алгоритм:
{ font-family: Tahoma; color: red }
{ color: red }
{ font-family: Tahoma; }
CSSO объединяет классы «.a» и «.b», что неправильно, там общее место — «color: red», оно короче, чем «font-family: Tahoma», объединять надо классы «.a» и «.c»!
Комментарий для skryzhanovsky.ya.ru:
Более того, должно получиться вот так:
.a, .c { font-family: Tahoma }
.b, .a { color: red }
Комментарий для Евгения Степанищева:
Такую минимизацию уже проходил в ветке 1.0.x, после чего срочно переделал на менее опасную. :) В общем случае (обрати внимание на «в общем случае») нельзя нарушать порядок следования селекторов, это может плохо закончиться при комбинации классов у элемента. В твоём примере из abc получился acba, что может сломать вёрстку.
PS. Добавил бы подчёркивание в разметку комментов. :)
Комментарий для skryzhanovsky.ya.ru:
Видимо, на ночь глядя, я не очень понимаю в чём опасность. Поясни на примере, пожалуйста!
Ну уж нет. Подчёркивание должно быть только в ссылках. :)
Комментарий для Евгения Степанищева:
Пожалуй, самый наглядный пример:
test0.css: .t0 { color: red } .t1 { color: green }
test1.css: .t1 { color: green } .t0 { color: red }
test.html: <div class=«t0 t1»>test</div>
В случае с test0.css получишь текст зелёного цвета, а test1.css даст красный цвет.
Пример кажется простым и очевидным. И кажется, что «ну хорошо, в таких случаях не менять порядок, конечно, но наверняка иногда можно», но кажется так ошибочно. Распространи проблему на большой CSS, на сотни селекторов и тысячи свойств, да ещё и на множественные комбинации классов у элемента. Попытка безопасно перетрясти такое выливается в адову лапшу «частных случаев» с постоянной опасностью упустить какой-нибудь сотый случай, когда нельзя было даже дышать на селектор.
Может быть, в будущем снова залезу и что-то получится, но пока целью является гарантированное сохранение результата рендеринга даже в ущерб потенциальному отгрызанию размера.
Комментарий для skryzhanovsky.ya.ru:
Не вижу как это пересекается с моим примером. Можешь показать как с моим примером может получиться что-то плохое?
Комментарий для Евгения Степанищева:
Жень, ведь специально выделил «в общем случае» и «может», а потом упомянул «частные случаи». В твоём примере ничего плохого. :)
На деле я просто не готов сейчас написать обстоятельно и убедительно на эту тему. Более того, вполне вероятно, что дую на воду после молока, и преувеличиваю опасности. Предлагаю вернуться к разговору после того, как CSSO исчерпает минимизации без смены селекторов, а я для этой области минимизаций составлю каталог.
Комментарий для skryzhanovsky.ya.ru:
Ok!
Комментарий для skryzhanovsky.ya.ru:
Отлично смотрится. Удачных поисков в оптимизации.
Всё бы замечательно, только ant`а таска для CSSO найти не удаётся...
Для YUI Compressor`а, например, есть (описан, например, на «Техногрете»: http://www.artlebedev.ru/tools/technogrette/soft/eclipse-ant/ ), и очень удобно настроив один раз проект, потом оптимизировать все файлы автоматом при внесении изменений.
А так — всё выглядит очень здорово! Спасибо Вашей конторе за такой замечательный проект!
Так что ждём, надеемся и верим! :)
Комментарий для se-la-vy.blogspot.com:
Таски (плагины) для Ant / Maven удобнее будет делать после того, как релиз CSSO портируется на Java. Потому не сейчас, но обязательно. :)
Спасибо.
Комментарий для skryzhanovsky.ya.ru:
Сергей, еще момент.
Пробел перед !important.
Комментарий для skryzhanovsky.ya.ru:
Ясно. А если через Rhino прогнать?