Опубликован: 16.02.2009 | Доступ: свободный | Студентов: 1437 / 138 | Оценка: 4.26 / 4.17 | Длительность: 16:10:00
ISBN: 978-5-9963-0024-2
Лекция 4:

Уменьшение числа запросов

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

Одним из возможных выходов из сложившейся ситуации будет использование характерных для 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-хаков (последнее предпочтительнее, ибо позволяет загружать всего один 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 и для него описанные обходные приемы уже теряют свою актуальность).

Включение музыки (base64)

Летом 2008 года весь мир облетела страница, содержащая реализацию первого уровня Super Mario Brothers. В нее, в общем, можно играть, хотя упущены многие ключевые аспекты (нет грибов, нет флага, нет повышающих очков и т. д.). Однако это, на самом деле, не самый интересный аспект этой разработки.

Наверное, больше всего интереса в этом веб-приложении представляет не сама игровая механика, а тот факт, что все собрано в одном-единственном файле — игровом скрипте, который включает все игровые спрайты и всю музыку. Это весьма любопытный образец использования технологий. Удивительно, но способ подключения музыки в этом приложении, пожалуй, наиболее простой для понимания. Приложение использует схему data:URI, которая кодирует музыкальные MIDI-файлы Mario в base64-виде. Результат очень просто найти в исходном файле:

aSounds = [
 // очень маленькая и простая тема Mario. Написал Mike Martel.
 "data:audio/mid;base64,TVRoZAAAAAYAAQAEAMBNVH...",
 // игра закончилась. Написал John N. Engelmann.
 "data:audio/mid;base64,TVRoZAAAAAYAAQADAHhNVH..."
],

data:URI работает следующим образом: все содержимое выбранного файла кодируется в виде одной строки в исходном файле. В результате мы получаем 1 файл вместо трех: одного исходного и двух внешних. В данном случае вся закодированная строка отправляется элементу <embed/>, который и отвечает за проигрывание MIDI-файла. Можно обнаружить конечный результат этих махинаций в следующем виде:

playMusic = function(iSoundID, bLoop) {
 if (!bMusic)
 	return;
 var oEmbed = dc("embed");

 oEmbed.src = aSounds[iSoundID];
 oEmbed.id = "sound_" + iSoundID;

 if (bLoop)
 	oEmbed.setAttribute("loop", "true");
 oEmbed.setAttribute("autostart", "true");

 oEmbed.style.position = "absolute";
 oEmbed.style.left = -1000;

 appChild(document.body, oEmbed);
},

Вышеприведенный код просто создает элемент embed, устанавливает для него автозапуск проигрывания музыки и в качестве источника данных указывает data:URI. В результате мы получаем MIDI-файл, который сам начинает проигрываться (предположительно, тот же результат мог быть получен при использовании другого универсального музыкального файлового формата — WAV).

Файлы в формате data:URI могут проигрываться всеми браузерами, за исключением Internet Explorer. Поэтому пользователи Internet Explorer просто не получают музыкального сопровождения для игры (хотя можно было бы только для IE подгружать его динамически, создавая тот же самый embed просто со ссылкой на внешний файл).

CSS Sprites и data:URI

Давайте проведем сравнение двух освещенных выше методов для кардинального уменьшения числа запрашиваемых файлов с сервера: CSS Sprites и data:URI.