Войти | Выйти

Conditional Comments

Казалось бы, что нового я могу Вам рассказать на эту тему? Ничего, — скажете Вы, — и будете абсолютно правы.
А я Вам не про сами CC расскажу, а про то, как мы их используем для подключения стилей.

Итак, как мы подключаем стили в проект и какие задачи при этом решаем?

  1. Сделать так, чтобы стили приходили минимизированые — экономим трафик пользователя и нашей компании
  2. Сделать так, чтобы IE-шные костыли выливались только для IE — решаем проблему валидности кода
  3. Сделать так, чтобы они выливались пользователю минимальным количеством http-запросов — решаем проблему скорости передачи данных с сервера.

Первые два пункта вообще тривиальны, говорить о них мне уже поздно. Много сказано до меня :)
Хочется поговорить о 3-ем.

Ещё полтора года назад мы загружали стили проекта так:

<link rel="stylesheet" href="project.css" />
<!--[if lte IE 7]><link rel=stylesheet href="project.ie.css" /><![endif]-->

Суть решения: project.css содержит в себе все необходимые («основные») селекторы, а в project.ie.css только костыли для IE, которые дополняют/переопределяют селекторы основного файла.

И если предположить, что файлы project*.css перед отдачей клиенту проходят минимизацию (удаление пробельных символов, переводов строк и комментариев), то вышеописанный код решает 2 задачи из 3.

Почему не 3?

Потому что в IE7 и ниже происходит два запроса — к project.css и project.ie.css. То есть лишний запрос. А это означает лишние байты на передаче заголовков, лишнее время ожидания. И ведь, не бог весть какие затраты. Вполне можно закрыть глаза на это?

Можно, «если вас не интересует результат» (с) М.М. Жванецкий ;-)
Если мы разрабатываем какой-то нагруженный ресурс, на который ожидается большой поток пользователей ежеминутно (!), то лишние 200 байтов запроса на один запрос превращаются в многие минуты ожидания получения данных от сервера в суточном масштабе.

Да и вообще, если есть способ отдавать байт меньше, почему это не делать?

Элегантное решение

Видимо, это очень хорошо понимал Алексей Тен, когда на ClientSide2007 после доклада Витали Харисова он рассказал, что существует известный элегантный способ отдавать IE один файл вместо двух. Вот что Алексей предложил:

<!--[if !IE]>--><link rel="stylesheet" href="project.css" /><!--<![endif]-->
<!--[if IE]><link rel=stylesheet href="project.ie.css" /><![endif]-->

И в начале файла project.ie.css пишется импорт project.css. Этим решается проблема копипаста «основных» селекторов в ie-шный файл.

@import url(project.css);

Как MSIE читает эти CC

В первой строке он встречает условный комментарий [if !IE], который понимает однозначно: «Мне нет дела до всего, что написано дальше до закрывающего [endif]«.

Во второй строке он видит условный комментарий, который выполняется согласно условию [if IE].

Обратите внимание на то, что правильный CC (неважно, с каким условием) начинается такой конструкцией:

<!--[if !IE]>

А у нас он записан c дополнительными символами -->:

<!--[if !IE]>-->

Лишние символы для IE безразличны, потому что IE игнорирует всё внутри этого CC, а для остальных браузеров эти символы формируют завершённый комментарий, благодаря чему все браузеры видят тег <link> и выполняют запрос к стилевому файлу.
Остроумный способ. Лёша, спасибо тебе за идею ;-)

Но на этом хитрости не закончились.

На горизонте замаячил IE8, в котором Microsoft пообещал полную поддержку CSS2.1.
Кстати, Вы верите? А мы, знаете ли, решили поверить. Но опыт общения с продуктами Microsoft заставляет следовать поговорке «доверяй, но проверяй». Поэтому мы, на всякий случай, подстелили соломку, спасибо Вадиму за светлую мысль.

Как результат веры в IE8, в группе HTML-верстки было принято решение разделять загрузку стилей не по схеме «IE/неIE», а по факту «поддерживаются стандарты или нет».

Мы договорились о такой классификации:

  1. Браузеры с поддержкой стандартов
    Firefox/Safari/Opera/Chrome/MSIE8
  2. Браузеры без поддержки стандартов
    MSIE 5-6-7

Как следует из классификации, мы поверили Microsoft на слово и отнесли IE8 к разряду «стандартных» браузеров, несмотря на то, что все существующие версии IE сейчас являются тем камнем, который тянет Web на дно. Правда, за полгода существования IE8 beta мы пару раз уже встречали странноватые баги в реализации CSS, но мы продолжаем верить. Очень хочется, просто :)

Как изменилась схема подключения?

Виталя взял идею, о которой рассказал Лёша, и модифицировал в такую:

<!--[if gt IE 7]><!--><link rel="stylesheet" href="project.css"/><!--<![endif]-->
<!--[if lt IE 8]><link rel=stylesheet href="project.ie.css"><![endif]-->

По-прежнему, первая строка выполняется браузерами первой группы (включая MSIE8), вторая строка — браузерами второй группы.
И снова задача «один браузер — один http-запрос к стилям» решена элегантно.

Внимательный читатель, конечно же, обратил внимание, что в первой строке есть какая-то странность?
В схеме Лёши было сказано:

<!--[if !IE]>-->

А в схеме Витали:

<!--[if gt IE 7]><!-->

Зачем писать лишние символы <!, можно прочитать по-русски, или по-английски прямо на msdn-е.

Вот теперь задача №3 может считаться не только выполненной, но и выполненной «на перспективу». То есть, когда (а когда-то ведь это произойдет) IE8 вытеснит своих предшествеников, мы быстро и очень «дёшево» оторвем довески для глючных IE 5-7 и забудем хаки, как страшный сон.

PS Неужели, это свершится на моем веку? *подняв глаза к небу*

PPS Описанная схема широко внедрена в Яндекс.Проекты и показала свою боеспособность. Можно пользоваться. Как говорит мой знакомый продавец овощей: «Покупай, картошка без проблем» ;-)

Не исключено, что Вы эту же схему придумали сами, независимо от Яндекса. В таком случае, жму Вашу руку за сообразительность и Светлые Идеи :)

Update
Как справедливо заметили Ольга и Антон, директива @import url(project.css); в файле project.css выполняет в браузере тот самый http-запрос, который мы стремимся избежать.
Да, во время разработки так и происходит — на сервер ходят лишние запросы и это не важно. И только когда мы выкладываем проект в production, мы осуществляем мероприятия по оптимизации CSS.

Фокус в том, что для production мы собираем стили. У нас есть перловый скрипт, который запускается на момент сборки production-версии. Он заглядывает в каждый CSS-файл, находит в нем директиву импорта и вместо нее подставляет код из файла, на который этот импорт смотрит. В результате получаем два файла project.css и project.ie.css, внутри которых уже нет ни одного импорта.

Комментарии

4 комментария к “Conditional Comments”

  1. engel 2.Мар.2009 в 12:41

    А разве @import url(project.css); — это не запрос?

    ОтветитьReply to this comment
  2. Вадим Макишвили 2.Мар.2009 в 12:46

    Отвечает на сообщение engel:
    Без всякого сомнения.
    Когда я сказал, что файлы проходят минизацию, я не уточнил, что перед ней они проходят сборку. Буквально, скрипт-сборщик ходит по файлу и вместо @import url(project.css); подставляет содержимое этого файла.

    На конференциях Виталя несколько раз (да и я однажды) сообщали об этом.

    ОтветитьReply to this comment
  3. smmurf 2.Мар.2009 в 12:54

    Если я правильно понимаю, то во втором файле (который содержит «костыли») в начало подключен «чистый» файл с помощью директивы @import?
    Но ведь эта директива обрабатывается не сервером, а клиентом, то есть прочтя ее браузер обращается на сервер и загружает указанный файл. Таким образом мы не убираем лишний http-запрос, а просто переносим его причину из html-файла в css-файл. И IE при этом по-прежнему за стилями обращается к серверу дважды.
    Чтобы избежать лишнего запроса необходимо файл для IE физически собрать из двух. Возможно, у вас это делается сервером «на лету», но если этот совет реализовать буквально на другом сервисе, выигрыша в количестве запросов это не даст.

    ОтветитьReply to this comment
  4. Вадим Макишвили 2.Мар.2009 в 13:26

    Отвечает на сообщение smmurf:
    Антон, я дописал Update к статье, в которой ответил на ваш вопрос.

    ОтветитьReply to this comment

Написать комментарий.




XHTML: Можете использовать следующий код: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam Protection by WP-SpamFree Plugin

Subscribe without commenting