Преимущества и недостатки data:URI

Вместе с техникой CSS Sprites (или как ее альтернатива) data:URI может существенно уменьшить число HTTP-запросов. Краткий список плюсов данного метода:

  • Экономят HTTP-запросы, предотвращают издержки, связанные с большим числом объектов.
  • Экономят число параллельных потоков: у браузеров есть ограничение (по спецификации HTTP/1.1, однако, Firefox, Opera и Safari несколько вольно его расценивают, в частности, позволяя настраивать этот параметр или значительно его увеличивая; о настройках браузеров можно прочитать в восьмой главе) на число одновременных соединений (подробнее рассказывается в пятой главе) с одним хостом.
  • Упрощают HTTPS-запросы и улучшают производительность при таком типе соединения.

Однако встроенные изображения (только data:URI) не поддерживаются в Internet Explorer 5-7. Текстовое base64-представление данных также занимает больше, чем бинарное изображение. В наших тестах base64-данные были на 39-45% больше бинарного аналога, но gzip-сжатие позволяет уменьшить разницу до 5-10%. Предварительная оптимизация изображений перед base64-кодированием позволяет уменьшить их размер пропорционально (о сжатии изображений было рассказано во второй главе).

Также существует ряд ограничений на размер встроенных изображений. От браузеров требуется поддерживать только URL длиной до 1024 байтов, в соответствии с вышеупомянутой спецификацией RFC. Однако, браузеры более либеральны к пользователям в том, что они принимают. Например, Opera и Firefox последних версий поддерживают data:URI примерно до 50 Кб (для IE8 этот предел составляет 32 Кб). Но все же эта техника подходит больше для небольших по размеру изображений. Краткий список минусов:

  • Не поддерживается IE до 7 версии включительно.
  • Требуются дополнительные действия для обновления внедренного содержания (перекодировать, еще раз вставить).
  • Ограничена длина. Не подходит для вставки больших изображений.
  • Изображения, представленные в base64-кодировке, примерно на 33% больше размера их бинарного аналога (на 10% при использовании сжатия).
  • Встроенные картинки (не в CSS) не получится закэшировать по определению. Они будут кэшироваться только с HTML-кодом.

Дополнительные соображения по оптимизации

Одним из возможных выходов из сложившейся ситуации будет использование характерных для IE CSS-хаков, чтобы только для него подключить фоновые изображения. В итоге, вышеприведенный пример будет выглядеть примерно так:

ul {
    list-style: none;
}
ul li { 
    margin: 0 0 1px; background: url(data:image/gif;base64,<?php 
    echo base64_encode(file_get_contents(“../images/flag.png “)) 
    ?>) top left no-repeat;
     height: 14px; 
    text-indent: 10px;

    * html ul li { background-image: url(/images/flag.png);

    *+html ul li { background-image: url(/images/flag.png);
}

Также возможно кодирование изображений, которые выводятся в base64, автоматически при изменении этих изображений (для этого потребуется простой скрипт, который проверяет, обновились ли соответствующие файлы; если обновились, то перезаписывает их представление в CSS-файле, заодно и меняет хеш-строку для подключения этого файла в HTML, чтобы избежать кэширования).

Для включения небольших графиков прямо в HTML-код прекрасно подойдут условные комментарии, когда для ряда браузеров изображение выводится в base64, а для остальных (например, для IE) подключается через условные комментарии. Например, так:

<!–[if !IE]>–>
<img src=”data:image/png;base64,…” alt=”График” title=”График”/>
<!–<![endif]–>
<!–[if IE]>
<img src=”chart.png” alt=”График” title=”График”/> <![endif]–>

Если использовать связку относительное позиционирование родителя – абсолютное позиционирование дочернего элемента, то IE будет просто выводить картинку из внешнего файла поверх непонятного (для него) объекта.

Кроссбраузерное использование data:URI

IE (до версии 7 включительно) не поддерживает протокол data:URI, а вместе с ним base64-кодирование внешних файлов и включение их прямо в тело необходимого документа (будь то HTML или CSS/JavaScript-файл). Однако если рассмотреть использование протокола mhtml (который, конечно же, аккуратно поддерживается только в IE), многое становится более ясным, и base64-кодирование удается применять в полной мере.

О, этот странный Microsoft!

В IE существует альтернативное решение для вставки изображений прямо в текстовый документ в виде mhtml-включений. Давайте остановимся на практической реализации для CSS-файлов (в данном случае это файл main.css):

/* Content-Type: multipart/related; boundary=”_”

_ Content-Location: 1
Content-Transfer-Encoding: base64
iVBOR..
*/

Далее в CSS-файле нужно лишь вызвать эту картинку следующим образом:

ul li { 
    background-image:url(mhtml:http://site.ru/main.css?20080531!1);
}

Здесь в адресе картинки идет протокол mhtml: (который поддерживается исключительно в IE, но это не так важно), далее полный URL до CSS-файла (который содержит эту картинку), в данном случае этот URL еще и содержит GET-параметр для соответствующего кэширования. И небольшая тонкость применения данного формата: необходимо в URL использовать ту же строку, что и в HTML-файле, в котором подключается данный CSS, иначе IE запросит CSS-файл дважды: первый раз как таблицу стилей, второй раз – как хранилище картинки. Далее после восклицательного знака (!) идет тот идентификатор, который мы назначили картинке в Content-Location. И все.

Объединяем несовместимое

С одной стороны, у нас схема data:URI, которая поддержана W3C и распознается всеми браузерами, кроме IE. С другой стороны, у нас IE, который понимает mhtml и с которым работают 70% наших пользователей. Мы объединим эти два решения, благо, они оба используют base64-представление картинок.

Задача первая: объединить оба назначения стилевых правил, чтобы они не конфликтовали друг с другом. Решается это очень просто с помощью либо отдельного CSS-файла для IE (через условные комментарии), либо CSS-хаков (последнее предпочтительнее, ибо позволяет загружать всего 1 CSS-файл). В итоге, в CSS-файле мы имеем примерно следующее:

/* Content-Type: multipart/related; boundary=”_”

_ Content-Location: 1
Content-Transfer-Encoding: base64
iVBOR..
*/
ul li { 
    background: #fff url(data:image/png;base64,iVBOR…) 0 0 no-repeat;

    * html ul li { 
        background-image: url(mhtml:http://site.ru/main.css?20081010!1);

    *+html ul li { 
        background-image: url(mhtml:http://site.ru/main.css?20081010!1);
}

Данная конструкция позволяет вывести фоновое изображение в base64-кодировке для всех (ну или 99,9%) браузеров. Почему в конце содержатся 2 разных CSS-селектора с одним объявлением? Первое предназначено для IE6 и предыдущих версий, второе – для IE7. Объединить через запятую их нельзя.

Панацея или ящик Пандоры?

Первый же скептический вопрос, который каждый читатель должен на этом месте задать: как можно эти картинки выводить только один раз в CSS-файле (можно заметить, что base64-строка фигурирует там дважды)? Ответ: никак. Однако выход есть.

Это gzip-сжатие. Если очень коротко, то при любом архивировании создается некоторый словарь наиболее часто используемых строк, которые заменяются на однобайтовые представления. При восстановлении данных из архива происходит обратная процедура, когда значения слов словаря подставляются вместо их наименований. В нашем случае у нас для одной картинки используется одна и та же base64-строка. Это означает, что при архивировании она сожмется удивительно эффективно.

Стоит также заметить, что при таком подходе результирующий сжатый файл занимает даже меньше, чем при назначении с помощью CSS-хаков для IE внешних изображений для загрузки (ведь используется меньше различных конструкций).

Валидность

Получившийся таким образом CSS-файл абсолютно валиден (так как все mhtml-вставки происходят в комментариях). Каждое CSS-объявление валидно с точки зрения CSS 2.1, и те небольшие приемы, которые позволяют эффективно сжать данные для всех браузеров, не отражаются на восприятии ими файла. Такой подход будет уместным только для CSS. Для HTML ситуация совсем другая (о ней немного подробнее ниже). HTML-документ при таком подходе оказывается невалидным, что выливается во множественные проблемы верстки, и это не та же цена, которую можно заплатить за некоторый не очень явный рост производительности.

Некоторые итоги

  • Картинки в CSS можно вставлять с помощью data:URI.
  • Для IE можно использовать mhtml, полностью дублирующий эту функциональность.
  • Для корректного применения стилей нужны CSS-хаки либо разделение CSS- файлов.
  • Используем gzip для CSS-файлов для устранения последствий множественного использования base64-строки.

Использовать base64-кодирование на ваших страницах можно уже прямо сегодня. Очень важно при этом понимать возможные последствия и, по возможности, обойтись малой кровью. Однако с выходом IE8 и массовым его проникновением ситуация кардинально изменится (ведь он поддерживает data:URI и для него описанные обходные приемы уже теряют свою актуальность).

Материалы близкой тематики:

Posted in Разгони свой сайт.