Опубликован: 16.06.2010 | Уровень: специалист | Доступ: платный
Лекция 5:

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

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

4.1.2. Объединение JavaScript-файлов

Для JavaScript-файлов весь описанный механизм повторяется, за исключением небольших деталей.

Получаем код

Во-первых, получать код мы будем уже немного другим методом, и для нас будет несущественен атрибут media:

$regex =
"!<script[^>]+type\\s*=\\s*(\"text/javascript\"|'text/javascript'
|text/javascript)([^>]*)>(.*?</script>)!is";
preg_match_all($regex, $this->head, $matches, PREG_SET_ORDER);
if (!empty($matches)) {
foreach($matches as $match) {
$file = array();
$file['tag'] = 'script';
$file['source'] = $match[0];
/* вырезаем из найденного куска HTML-кода обрамляющие теги,
чтобы идентифицировать внутренние скрипты */
$file['content'] = preg_replace("/(<script[^>]*>
[\t\s\r\n]*|[\t\s\r\n]*<\/script>)/i", "", $match[0]);
$file['file'] = '';
preg_match_all("@(type|src)\s*=\s*(?:\"([^\"]+)\"|'([^']+)'
|([\s]+))@i", $match[0], $variants, PREG_SET_ORDER);
if(is_array($variants)) {
foreach($variants AS $variant_type) {
$variant_type[1] = strtolower($variant_type[1]);
$variant_type[2] = !isset($variant_type[2]) ?
(!isset($variant_type[3]) ? $variant_type[4] :
$variant_type[3]) : $variant_type[2];
switch ($variant_type[1]) {
case "src":
$file['file'] =
trim($this->strip_querystring($variant_type[2]));
$file['file_raw'] = $variant_type[2];
break;
default:
$file[$variant_type[1]] = $variant_type[2];
break;
}
}
}
$this->initial_files[] = $file;
}
}
Листинг 4.5.
Объединяем

Тут нас ждет еще одно отличие: разные куски JavaScript-кода лучше объединять через точку с запятой с переводом строки. Ибо предыдущая часть кода может не оканчиваться на точку с запятой, потому мы обязаны как-то отделить ее от последующей.

Далее в ходе объединения было установлено, что файлы библиотек для визуального форматирования кода (в силу своей сложности) мало приспособлены к объединению с другими файлами. Поэтому рекомендуется при объединении избегать следующих файлов: tinymce.js и fckeditor.js. Во всем остальном механизм абсолютно тот же самый, что и для CSS-файлов (за исключением отсутствия необходимости разрешить @import и необходимости заменять пути для фоновых изображений и ресурсов).

Минимизируем

Для минимизации JavaScript-кода лучше всего использовать уже имеющиеся на рынке решения: JSMin (http://www.crockford.com/ javascript/jsmin.html, который портирован в том числе и на PHP) или YUI Compressor (http://developer.yahoo.com/yui/compressor/). Про последний уже было написано чуть выше (параметры для запуска те же самые). В случае с JSMin все тоже довольно просто: нам нужно загрузить последнюю версию (http://code.google.com/p/jsmin-php/), подключить ее и просто вызвать минимизацию заданного файла:

require 'jsmin-1.1.1.php';
echo JSMin::minify(file_get_contents('example.js'));
Листинг 4.6.

Стоит также упомянуть, что классический JSMin не поддерживает условную компиляцию для IE. Поэтому тут нужно воспользоваться модифицированным решением (например, из исходных кодов Web Optimizer).

4.1.3. Заключение

Объединение текстовых файлов способно значительно ускорить загрузку вашего сайта, не причиняя вреда качеству разработки (вы можете разрабатывать отдельно и в автоматическом режиме выкладывать на сайт уже готовые версии файлов). Как показывают данные с webo.in, на произвольном сайте в Рунете используется 2,7 файлов стилей (средний размер — 5,5 Кб) и 5 JavaScript-файлов (средний размер — 15 Кб). Простое их объединение позволит выиграть 0,5-1 с при загрузке страницы. А минимизация (вместе с gzip-сжатием, уменьшающим размер на 85%) — еще 60 Кб, что составит 0,6 с при скорости подключения 100 Кб/с.

Как мы видим, совершенно простые действия способны значительно ускорить загрузку вашего сайта.

4.2. Алгоритм разбора и сбора CSS Sprites

В книге "Разгони свой сайт" был опубликован довольно подробный обзор технологии CSS Sprites. После внедрения ее на нескольких сотнях рабочих проектов удалось собрать некоторый набор наиболее часто возникающих проблем при использовании CSS Sprites и методов их решения. Также в этом разделе рассматривается прикладной способ по автоматизации создания CSS Sprites для произвольного проекта.

4.2.1. Обзор технологии


CSS Sprites, на самом деле, — всего лишь расширенное использование технологии background, заложенной еще в спецификации CSS1. Все, до чего додумалось прогрессивное человечество за эти годы, — это множественный фон у элементов (как он будет совместим с концепцией CSS Sprites, еще придется проверить). Основные свойства, которые мы используем для задания фонового изображения:

  • background-image — основная "рабочая лошадка". Именно за ней будущее в виде data:URI, который в конце концов победит CSS Sprites. Но произойдет это еще не скоро;
  • background-repeat — вторая не менее важная составляющая при использовании фонового изображения. Ведь задавая no-repeat для данного свойства, мы намеренно подчеркиваем, что допустимо использование CSS Sprites для "склейки" изображений (по умолчанию используется repeat);
  • background-position — "волшебная палочка" для CSS Sprites, позволяющая спрятать или показать определенные части фонового изображения.

Кроме заявленных свойств также есть еще несколько (например, background-color), но они к спрайтам имеют посредственное отношение. Однако стоит добавить к нашим объектам, с помощью которых мы будем формировать карты изображений, размеры (в относительных или абсолютных единицах) и отступы (padding). Это позволит точнее разделить изображения по группам и корректно расположить их в финальном комбинированном изображении.

4.2.2. Прикладные тонкости

Естественно, рассматривая набор возможных эффектов при использовании фоновых изображений, стоит отметить следующие:

  • Объект, полностью заполненный фоновым изображением.

    Здесь основную роль играют конечные размеры объекта (разумеется, если изображение не повторяется по всем осям сразу: в таком случае использовать его для CSS Sprites не представляется возможным). Довольно часто фон под объектом может меняться в зависимости от каких-либо условий (преднамеренный акцент или действия со стороны пользователя), но для логики создания CSS Sprites это несущественно. Здесь же можно выделить три подслу-чая: соответствующих неповторяющемуся фону и повторяющемуся по оси X или Y.

  • Фоновое изображение заполняет не весь предоставленный ему объем (либо размеры объекта не заданы, либо заданы в относительных — em, % — единицах). Тут нам необходимо прикреплять повторяющееся изображение "в конец" спрайта, чтобы на той части объекта, что осталась без фонового изображения, не проявлялось никаких дефектов. Либо (в случае no-repeat) расположить изображения "лесенкой" (это особенно актуально в случае фона для элементов списка). Стоит отметить, что в зависимости от значения background-position CSS Sprites здесь могут быть как возможны, так и невозможны в принципе (например, в случае 100% 100%). Тут можно выделить еще несколько случаев, различающихся по background-position, background-repeat и линейными размерами блока.
  • Изображение является анимированным. Поскольку далее речь пойдет о применении PNG- и JPEG-изображений для CSS Sprites, то анимированные изображения придется сразу выбросить из рассмотрения: поддержка анимированных PNG-изобра-жений находится сейчас на самом зачаточном уровне в браузерах.

Все описанные примеры можно более четко структурировать по следующим группам:

  1. background-repeat: no-repeat, background-position: абсолютные числа и заданы линейные абсолютные размеры.
  2. background-repeat: no-repeat, background-position: абсолютные числа, линейные размеры не заданы или заданы в относительных единицах.
  3. background-repeat: repeat-x, задана высота элемента.
  4. background-repeat: repeat-x, высота элемента не задана.
  5. background-repeat: repeat-y, задана ширина элемента.
  6. background-repeat: repeat-y, ширина элемента не задана.
  7. background-repeat: no-repeat, background-position: 100% 0, задана высота элемента (в абсолютных единицах).
  8. background-repeat: no-repeat, background-position: 0 100%, задана ширина элемента (в абсолютных единицах).
  9. background-repeat: no-repeat, background-position: 100% 0, высота элемента не задана (или задана в относительных единицах).
  10. background-repeat: no-repeat, background-position: 0 100%, ширина элемента не задана (или задана в относительных единицах).
  11. background-repeat: repeat.
  12. background-repeat: repeat-x или background-repeat: repeat-y, размеры элемента указаны в относительных единицах.
  13. background-repeat: no-repeat, background-position: относительные единицы.
  14. изображение является анимированным GIF-файлом.

Глядя на эту спецификацию, становится в общем понятно, в каком направлении двигаться для автоматизации создания CSS Sprites. Стоит только отметить, что при использовании одного и того же изображения многими CSS-селекторами нужно отследить background-position и устранить изначальные CSS Sprites, задействованные в стилях. Процесс получения изображений из готовых CSS Sprites в автоматическом режиме достаточно сложен и может быть применим только на локальных проектах.

4.2.3. Практическое решение: CSS Tidy

Далее речь пойдет уже об инструменте Auto Sprites (http://sprites.in/), который был положен в основу разработки Web Optimizer (http://www.web-optimizer.ru/). После описанных выше умозаключений оставались чисто технические вопросы: как все это закодировать и как отладить полученное решение.

Для начала нам нужно разобрать все дерево CSS-правил, потом отобрать из них относящиеся к фоновым изображениям и линейным размерам объектов, а уже потом произвести над ними требуемые действия. Идеально для этой задачи подходит CSS Tidy (http://csstidy.sourceforge.net/), который был замечательно испробован, протестирован и улучшен после интеграции на сотнях реальных сайтов. CSS Tidy представляет собой набор PHP-библиотек, которые могут быть включены в произвольный проект для каких-либо действий над заданным набором стилевых правил.

Ниже приводится простой алгоритм выделения из массива CSS-правил нужных нам свойств фона на языке PHP:

/* $this — объект класса css_sprites */
/* сначала создадим новый объект CSS Tidy, используя заданный
$css_code*/
$this->css = new csstidy();
$this->css->parse($css_code);
/* определим CSS-свойство для элементов без фона, */
/* чтобы не переопределить его в ходе преобразований */
$this->none = 'none!important';
/* далее мы переберем весь массив на предмет сначала
@media-конструкций*/
foreach ($this->css->css as $import => $token) {
/* создадим для каждого заданного @media свой массив правил */
$this->media[$import] = array();
/* а затем и самих CSS-селекторов */
foreach ($token as $tags => $rule) {
/* получим для каждого набора селекторов массив CSS-свойств и их
значений */
foreach ($rule as $key => $value) {
/* выделим из всех свойств только относящиеся к фону */
if (preg_match("/background/", $key)) {
/* переопределим "выключенный" фон, чтобы не затронуть */
/* в ходе преобразований */
if ($key == 'background' && $value == 'none') {
$this->css->css[$import][$tags]['background'] =
$this->none;
}
/* теперь для каждого отдельного CSS-селектора */
foreach (split(",", $tags) as $tag) {
/* создаем отдельный объект */
if (!empty($this->media[$import][$tag])) {
$this->media[$import][$tag] = array();
}
if ($key == 'background') {
/* получаем массив фоновых свойств из CSS-свойства background */
$background = $this->css->optimise-
>dissolve_short_bg($value);
foreach ($background as $bg => $property) {
/* пропускаем свойства, заданные по умолчанию */
if (
/* в частности, для background-position */
!($bg == 'background-position' &&
($property == '0 0 !important' ||
$property == 'top left !important' ||
$property == '0 0' ||
$property == 'top left')) &&
/* для background-origin */
!($bg == 'background-origin' &&
($property == 'padding !important' |
$property == 'padding')) &&
/* для background-color */
!($bg == 'background-color' &&
($property == 'transparent !important' ||
$property == 'transparent')) &&
/* для background-clip */
!($bg == 'background-clip' &&
($property == 'border !important' ||
$property == 'border')) &&
/* для background-attachement */
!($bg == 'background-attachment' &&
($property == 'scroll !important' ||
$property =='scroll')) &&
/* для background-size */
!($bg == 'background-size' &&
($property == 'auto !important' ||
$property == 'auto')) &&
/* и для background-repeat */
!($bg == 'background-repeat' &&
($property == 'repeat !important' ||
$property == 'repeat'))) {
/* Переопределяем background-image, если оно не задано */
if ($bg == 'background-image' &&
($property == 'none !important' ||
$property == 'none')) {
$property = $this->none;
}
/* и дополняем background-position, вместо left выставляем
left center, вместо right — right center,
и ряд других исправлений */
if ($bg == 'background-position') {
$property =
$this->compute_background_position
($property);
}
/* В конце выставляем полученные значения для массива правил,
определяющих исходные фоновые картинки */
$this->media[$import][$tag][$bg] = $property;
}
}
/* Если у нас задано детальное CSS-свойство, то просто его на-
значаем */
} else {
/* Дополняем background-position, вместо left выставляем left
center,
а вместо right — right center,
также меняем местами bottom right и некоторые другие случаи */
if ($key == 'background-position') {
$value =
$this->compute_background_position($value);
}
/* и выставляем "исправленные" свойства для массива CSS-правил,
пропуская свойства по умолчанию */
if ($key != 'background-position' || $value != '0 0') {
$this->media[$import][$tag][$key] = $value;
}
}
}
}
/* завершаем цикл перебора исходных CSS-правил */
}
}
}
Листинг 4.7.
< Лекция 4 || Лекция 5: 12345 || Лекция 6 >
Ольга Артёмова
Ольга Артёмова

Доброго времени суток!

Прошла курс, но почему-то диплом получить не могу, хотя курс значится завершенным, хотя обязательные два модуля пройдены. Как решить эту проблему?

Сертификация: оптимизация и продвижение web-сайтов.

Ярославй Грива
Ярославй Грива
Россия, г. Санкт-Петербург
Ёдгор Латипов
Ёдгор Латипов
Таджикистан, Кургантепа