Вопрос скорости загрузки веб-страниц привлекает внимание всех веб-разработчиков уже очень давно — практически с того момента, как в HTML-документе появились картинк
За последние 10 лет уже многократно менялся сам подход к созданию сайтов. В эпоху браузерных войн и ограниченного доступа по модему наиболее важными аспектами клиен
Но ситуация изменилась. Сейчас средняя веб-страница уже крайне тяжело вписывается в установленные когда-то рамки «загрузка за 10 секунд на модеме». В среднем на ней
Данное издание старается объединить в себе все современные подходы к построению высокопроизводительных веб-приложений и просто веб-сайтов, которые быстро загружа
Кроме теоретических аспектов производительности приведено также большое количество практических рекомендаций, примеров конфигурационных файлов, различных прие
Web Optimizator
Идея организовать ресурс, как посвященный теоретическим аспектам оптимизации времени загрузки веб-страницы, так и предлагающий online-инструменты для этой самой опт
За основу online-инструмента были взяты замечательные примеры с
Именно с этой целью и был создан Web Optimizator ( http://webo.in/ ).
Благодарности
Книга не увидела бы свет без помощи, советов и рекомендаций огромного количества людей. Каждый из них добавил что-то новое в нижеизложенный материал, поэтому у меня
Во-первых, хочу высказать персональную благодарность Виталию Харисову за несколько очень своевременных замечаний относительно производительности CSS-движка в бра
Во-вторых, нельзя не упомянуть Павла Димитриева и его замечательный перевод классических советов от группы разработчиков Yahoo! (
Значительный вклад в продвижение идей «ненавязчивого» JavaScript и обратной совместимости в работе веб-сайтов (когда пользователь может взаимодействовать со странице
Также необходимо упомянуть Евгения Степанищева ( http://bolknote.ru/
Естественно, нельзя обойти вниманием всех моих соратников по российскому крылу Web Standards Group (
Кроме этого Антон Лобовкин ( http://anton.lobovkin.ru/ ), Денис Кузнецов
И конечно, обязательно хочу поблагодарить всех пользователей
Глава 1. Что такое клиентская оптимизация?
1.1. Цели и задачи оптимизации
Каждая веб-страница состоит из основного HTML-файла и набора внешних ресурсов. Говоря о размере страницы (или сайта), очень часто имеют в виду размер именно первого фа
Рис.1 .1 . Тенденция изменения размера страницы и числа объектов для сайтов, проверяемых через Web Optimizator в 2008 году
В настоящее время на каждой странице вызывается несколько десятков внешних объектов, а размер исходного файла составляет не более 5% от общего размера. Как показали
Естественно, что технологии эти не ограничиваются сжатием текстовых (HTML, CSS, JavaScript) файлов на стороне сервера. Как несложно понять, основную часть внешних объектов н
Основные задачи оптимизации
Если говорить кратко, то можно выделить 3 основных задачи клиентской оптимизации:
Оптимизация размера файлов.
Оптимизация задержек при загрузке.
Оптимизация взаимодействия с пользователем.
Краткий обзор технологий
При этом все основные методы можно разбить на 6 групп (каждая из которых позволяет решить одну из заявленных задач).
Уменьшение размера объектов. Здесь фигурируют сжатие и методы оптимизации изображений, подробнее об этом можно прочитать во второй главе.
Особенности кэширования , которые способны кардинально уменьшить число запросов при повторных посещениях, раскрываются в третьей главе.
Объединение объектов. Основными технологиями являются слияние текстовых файлов, применение
Параллельная загрузка объектов , влияющая на эффективное время ожидания каждого файла. В пятой главе помимо этого приведены примеры балансировки запросов со стороны клиентского приложения.
Оптимизация CSS - производительности , что проявляется в скорости появления первоначальной картинки в браузере пользователя и скорости ее дальнейшего изменения. О CSS-производительности рассказывает ш
Оптимизация JavaScript . Есть достаточно много проблемных мест в JavaScript, о которых необходимо знать при проектировании сложных веб-приложений. Обо всем этом можно прочитать в седьмой главе.
Хочется отметить, что, несмотря на всю сложность затрагиваемой темы, первоначального ускорения загрузки веб-страницы можно добиться в несколько очень простых шаго
Все советы в книге упорядочены по увеличению сложности внедрения и уменьшению возможного выигрыша при загрузке страницы. Для простых веб-проектов можно ограничит
1.2. Психологические аспекты производительности
Терпимое время ожидания
Эффекты медленной скорости загрузки
Как время ответа сайта влияет на пользовательскую психологию
1.3. Стадии загрузки страницы
Рис. 1. 2. Стадии загрузки страницы
Расставляем приоритеты
Узкие места
1.4. Клиентская и серверная оптимизация: сходство и различия
Кэширование во главу угла
Меньше запросов — легче серверу
Архивировать и кэшировать на сервере
Кто у кого на службе?
1.5. Применение в разработке приложений
Пользователи обычно не знают, какие подходы применяются при разработке, как настроен сервер, какие клиентские и серверные средства разработки используются. Для ни
Ниже рассказывается, как можно организовать создание веб-приложения, ориентируясь на самые важные аспекты клиентской оптимизации.
Этап 1: Доставка информации и оформления
На этом этапе разработчики должны сделать все возможное, чтобы не замедлить скорость загрузки страницы. Фактически идет речь об ускорении первой стадии загрузки. Н
При загрузке страницы браузер запросит все CSS-файлы, объявленные в head страницы, последовательно. Поэтому каждый файл добавляет задержку в загрузке, равную времени з
Итог первого этапа — это доставленный и оформленный HTML. И издержки на доставку JavaScript сведены к минимуму (на этом этапе он только мешает, поскольку замедляет отображ
Этап 2: Кэширование файлов оформления и параллельные запросы
На данном этапе разработчики должны обеспечить быструю загрузку других страниц сайта (если посетитель решит туда перейти). Этот этап должен проходить параллельно
Одно или несколько дополнительных зеркал для выдачи статических ресурсов легко настраиваются в конфигурации, однако внедрить это в схему публикации изменений гор
CSS Sprites достаточно трудоемки в автоматической «склейке», поэтому их внедряют обычно на первом этапе (при создании макета страниц). При использовании метода data:URI на первом
Этап 3: Жизнь после загрузки страницы
Целью данного этапа является создание различных обработчиков событий, которые должны взаимодействовать с пользователем. Это могут быть и всплывающие подсказки, и
Говорят, что иногда «грамм видимости важнее килограмма сути» — это как раз про JavaScript. Ведь именно на нем можно реализовать механизмы, упрощающие действия пользоват
К этому моменту мы должны иметь оформленную HTML-страницу, на которой все ссылки и формы
У нас должны быть готовы серверные интерфейсы для AJAX-запросов; структура страницы должна быть такой, чтобы для аналогичных кусков HTML-кода не приходилось реализовыв
Чтобы не уменьшать скорость доставки контента и оформления, JavaScript-файлы (лучше всего, конечно,
Задача по обеспечению взаимодействия пользователя с интерфейсом сайта сводится к выполнению следующих действий:
Все это напрямую следует из концепции «ненавязчивого» JavaScript, которая описана в седьмой главе.
Поиск необходимых DOM-элементов должен нам дать список названий JavaScript-компонентов. Названия компонентов должны однозначно соответствовать названиям файлов на серв
Список названий компонент можно объединить в один запрос к серверу. В итоге на стадии пост-загрузки должны осуществляться запросы к файлам вида
У данного подхода есть два недостатка:
В папке /jas/ (которую мы, например, используем для кэширования наиболее частых вариантов подключения модулей) через некоторое время может оказаться очень много файл
Иногда на странице может оказаться очень много компонент, причем так много, что длина имени запрашиваемого объединенного файла перевалит за возможности файловой с
Этап 4: Предупреждаем действия пользователя
Если после посещения главной страницы большинство пользователей попадают внутрь сайта, то логично будет после полной загрузки главной страницы запрашивать стили
Аналогично можно рассмотреть и предзагрузку некоторых наиболее часто используемых картинок, которые отсутствуют на главной странице, и дополнительных JavaScript-моду
Естественно, что за балансировку третьей и четвертой стадий отвечает уже JavaScript-разработчик и фронтенд-архитектор — ведь именно в зоне ответственности последнего
Глава 2. Уменьшение размера
2.1. Насколько ресурсоемко архивирование HTML
Издержки на использование mod_gzip
Формализация модели
Набор тестов
Результаты тестирования
Пара слов о файловой системе
Что быстрее: gzip или канал?
Исследование степени gzip-сжатия и загрузки процессора
Рассмотрим далее, насколько сильно издержки на gzip зависят от степени сжатия, и как их прогнозировать с учетом всех остальных параметров. Новая серия тестов была нап
Как и ранее, на сервере проводились серии тестов по 10000 итераций в каждом. Замерялось время работы gzip при различных степенях сжатия. Затем оно усреднялось по серии, и
Рис. 2.5 . Издержки на gzip от степени сжатия
Далее график эффективности полученного сжатия (в % от оригинального размера файлов) от степени сжатия.
Рис. 2.6 . Эффективность различных степеней gzip-сжатия
Окончательные выводы
Конфигурируем Apache 1.3
Конфигурируем Apache 2
2.2. CSS и JavaScript в виде архивов
Статическое архивирование в действии
Проблемы для Safari
Конфигурируем Apache
Маленькие «но»
Два слова о nginx
2.3. Все о сжатии CSS
Инструменты
Графические результаты
Рис. 2.7 . Эффективность различных инструментов для минимизации CSS-файлов по сравнению с
Рис. 2.8 . Эффективность различных инструментов для минимизации CSS-файлов вместе с дополнительным архивированием по сравнению с
Рис. 2.9 . Эффективность различных инструментов для минимизации CSS-файлов вместе с дополнительным архивированием, увеличенный масштаб
Выводы
Практический пример
2.4. JavaScript: жать или не жать?
Инструменты и методика
Графические результаты
Рис. 2 .10 . Эффективность различных инструментов для минимизации JavaScript-файлов вместе по сравнению с gzip
Рис. 2.1 1 . Эффективность различных инструментов для минимизации JavaScript-файлов вместе с дополнительным архивированием по сравнению с
Рис. 2.12 . Эффективность различных инструментов для минимизации JavaScript-файлов вместе с дополнительным архивированием, увеличенный масштаб
Промежуточные выводы
Есть ли жизнь после сжатия?
Скорость загрузки JavaScript-библиотек
Методы упаковки JavaScript
Вариант
Среднее время
Уменьшенный
519.7214
Упакованный
591.6636
Нормальный
645.4818
Производительность загрузки JavaScript-библиотек
Инструментарий
Среднее время
jquery-1.2.1
732.1935
dojo-1.0.1
911.3255
prototype-1.6.0
923.7074
yahoo-utilities-2.4.0
927.4604
protoculous-1.0.2
1136.5497
Инструментарий
Среднее время
yahoo-utilities-2.4.0
122.7867
Jquery-1.2.1
131.1841
prototype-1.6.0
142.7332
dojo-1.0.1
171.2600
protoculous-1.0.2
276.1929
2.5. PNG против GIF
Алгоритмы сжатия
Возможности PNG
Поддержка PNG в браузерах
PNG и проблема соответствия для фоновых CSS-изображений
Анимированные PNG: MNG против "PNG+"
Двигаемся к маленьким PNG
Полезные советы
Ниже приведено несколько простых советов, как текущие изображения можно дополнительно уменьшить в размере. Можно написать простой скрипт, который перебирает дире
Преобразовывает GIF в PNG (и проверяет, есть ли при этом выигрыш):
или так
Уменьшает PNG-файлы в размере:
если при этом нужно удалить и gAMA-чанк, то:
если при этом хотим удалить другие чанки, отвечающие за цветовую коррекцию, то:
Уменьшает JPEG-файлы в размере (без потери качества):
Под Windows для уменьшения PNG-изображений можно использовать
Для отдельно взятой страницы общий размер изображений может быть уменьшен на 20–30% только благодаря следованию этим простым советам.
2.6. Разгоняем favicon.ico — это как?
Краткое описание формата
Боевое крещение
Оптимальные размеры
Палитра
Размер (в байтах)
2 бита
198
4 бита
318
8 бит
1406
24 бита
894
32 бита
1150
PNG — быть или не быть?
А если еще и сжать?
data:URI нас спасет?
Заключение
2.7. Режем cookie
Оптимизируем размер, зону и время действия
Хостинг для компонентов без cookie
Глава 3. Кэширование
3.1. Expires, Cache-Control и сброс кэша
Настройка заголовка HTTP Expires
Спецификация кэширования
Практическое запрещение кэширования
Разрешение кэширования
Форсированный сброс кэша
3.2. Кэширование в IE: pre-check, post-check
Спецификация
Рассматриваем подробнее
Рис. 3.1 . Диаграмма работы pre-check и post-check
Пример использования
3.3. Last-Modified и ETag
Давайте рассмотрим, какие существуют альтернативы прямому ограничению повторных загрузок ресурсов со стороны сервера и сохранению их в пользовательском кэше.
Last-Modified
Дополнительно к заголовку Cache-Control, который предупреждает браузер, что последний может не запрашивать исходный документ с сервера некоторое время, будет полезно пр
Как это работает? Дополнительно к периоду кэширования сервер может также отправить заголовок Last-Modified, который будет обозначать время последнего изменения файла на
Данная схема позволяет экономить время, затрачиваемое на передачу данных, однако при ее использовании браузер все равно будет устанавливать соединение с сервером
ETag
ETag ( англ . Entity Tags — тэги сущностей) — механизм, который используют браузеры и веб-серверы, чтобы определить, является ли объект, находящийся в кэше браузера, таким же, как соответств
Позднее, если браузер хочет определить актуальность компонента, он передает заголовок If-None-Match для передачи ETag обратно на сервер. Если ETag совпадают, ответ от сервера
Включить ETag для Apache можно, например, следующей директивой в конфигурации:
При этом ETag будет сформирован из даты изменения файла и его размера.
Синхронизация ETag и Last-Modified
Проблема ETag состоит в том, что обычно они используют атрибуты, специфичные в пределах одного сервера. ETag не совпадут, если браузер загрузит компонент страницы с одн
Apache 1.3 и 2 генерирует ETag в формате inode-size-timestamp. Даже если один и тот же файл на разных серверах лежит в одной и той же папке, имеет те же права, размер и время, номер его иногда
IIS 5.0 и 6.0 имеют похожий формат ETag: Filetimestamp:ChangeNumber. ChangeNumber — внутренняя переменная IIS для отслеживания изменений в конфигурации самого IIS, и нет гарантии, что эта переменна
В результате ETag, которые сгенерирует Apache или IIS для одного и того же файла, будут отличаться на разных серверах. Если ETag не будут совпадать, пользователь не будет пол
Если сайт находится только на одном сервере, это не будет большой проблемой. Но если вы используете несколько серверов с Apache или IIS, устанавливающие ETag в соответстви
Если вы не получаете всех преимуществ, которые предоставляет ETag, тогда лучше совсем отключить его. Тег Last-Modified позволяет проверять актуальность компонента на основ
в конфигурационный файл сервера.
3.4. Кэширование в iPhone
Попадание в кэш
Сжатые компоненты
После перезагрузки
Заключение
Глава 4. Уменьшение числа запросов
4.1. Объединение HTML- и CSS-файлов
CSS- файлы в начале страницы
Объединение CSS-файлов
Практическое решение
Два слова об условных комментариях
4.2. Объединение JavaScript-файлов
Все внешние JavaScript-файлы с сайта можно слить в один большой, загружаемый только один раз и навсегда. Это очень удобно: браузер не делает тысячу запросов на сервер для
Как всегда, в бочке меда есть ложка дегтя: в объединенный файл попадает много того, что при первом запросе можно было бы и не загружать. Чаще всего для борьбы с этим п
Конструктивные предложения
Для начала стоит разобрать используемый фреймворк на составные части. JSON — отдельно, AJAX — отдельно, работа с DOM — отдельно, формы — отдельно. После этого задача «вы
Информацию о зависимостях между составными частями можно хранить в удобном для автоматического использования виде. (Формы используют функции DOM, JSON — AJAX и так дале
Также можно хранить информацию о том, какие именно модули нужны сайту в целом. Используется ли AJAX? Если ли формы? Может быть, какие-то необычные элементы управления?
Да, естественно, все это можно и нужно автоматизировать.
В теории
С формальной точки зрения, после того как первые два предложения воплощены в жизнь, у нас появляется дерево зависимостей. Например, такое:
Дальше мы выбираем непосредственно нужные сайту вершины. Пусть это будут dom.js и animated.pane.js. Теперь это дело техники — обойти получившийся набор деревьев в глубину:
... удалить повторяющиеся элементы:
и слить соответствующие модули воедино.
На практике
Хранить информацию о зависимостях можно, например, следующим образом (добавляя в «модули» служебные комментарии):
Выделить подобные метки из текстового файла не составляет труда. Естественно, чтобы получить полное дерево зависимостей, надо будет пройтись по всем доступных фай
К чему мы пришли?
Затратив один раз кучу времени на формирование модулей и зависимостей между ними, мы экономим время
Итак, мы оставили нового пользователя наедине с единственным JavaScript-файлом, не включающим ничего лишнего. Стал ли при этом пользователь счастливее? Ничуть. Наоборот
Немного из теории HTTP-запросов
Время загрузки ресурса через HTTP-соединение складывается из следующих основных элементов:
Итак, время загрузки страницы будет состоять из времени загрузки HTML-кода и всех внешних ресурсов: изображений, CSS- и JavaScript-файлов. Основная проблема в том, что CSS и JavaS
Общие временные затраты при этом составят 3(T1+L) + R(A+B+C).
Объединяя файлы, мы уменьшаем количество запросов на сервер:
Очевидна экономия в 2(T1 + L).
Для 20 ресурсов эта экономия составит уже 19(T1 + L). Если взять достаточно типичные сейчас для домашнего/офисного Интернета значения скорости в 256 Кбит/с и пинга ~20-30 мс,
На первый взгляд, теория говорит, что загрузка страниц должна стать быстрее. В чем же она
Суровая реальность
Пусть у нашего сайта есть три страницы — P1, P2 и P3, поочередно запрашиваемые новым пользователем. P1 использует ресурсы A, B и C, P2 — A, С и D, а P3 — A, С, E и F. Если ресурсы не о
Если мы слили воедино абсолютно все JavaScript-модули сайта, получаем:
Результатом становится увеличение времени загрузки самой первой страницы, на которую попадает пользователь. При типовых значениях скорости/пинга мы начинаем прои
Если мы объединили только модули, необходимые для текущей страницы, получаем следующее:
Каждая отдельно взятая страница при пустом кэше будет загружаться быстрее, но все они вместе — медленнее, чем в исходном случае. Получаем, что слепое использование
Возможное решение
Конечно же, выход из сложившегося положения есть. В большинстве случаев для получения реального выигрыша достаточно выделить «ядро» — набор модулей, используемых
Вдумчивый читатель сейчас возмутится и спросит: «А что, если ядра нет? Или ядро получается слишком маленьким?». Ответ: это легко решается вручную выделением 2-3 незав
Реализация на PHP
PHP Speedy
4.3. Техника CSS Sprites
Простой rollover-эффект
Рис. 4.1 . Пример фонового изображения для простого rollover-эффекта. Источник: www.websiteoptimization.com
Сложный rollover-эффект
Рис. 4.2 . Пример фонового изображения для сложного rollover-эффекта. Источник: www.spegele.com
Проблемные места в IE
CSS Image map
Рис. 4.3 . Пример изображения для CSS Image Map. Источник: www.acronis.com
Статичные картинки
Рис. 4.4 . Пример фонового изображения «все-в-одном». Источник: webo.in
Онлайн-генераторы
Полезные советы
Рис. 4.5 . Пример фонового изображения с расположением картинок «лесенкой». Источник: webo.in
4.4. Картинки в теле страницы с помощью data:URI
Поддержка браузерами
Схема data:URI
Рис. 4.6 . Пример изображения, вставленного с помощью data:URI. Источник webo.in
CSS и встроенные изображения
Проблемы data:URI
Работа в Internet Explorer
Преимущества и недостатки data:URI
Дополнительные соображения по оптимизации
Кроссбраузерное использование data:URI
О, этот странный Microsoft!
Объединяем несовместимое
Панацея или ящик Пандоры?
Валидность
Некоторые итоги
Включение музыки (base64)
4.5. CSS Sprites и data:URI
Проблемы при верстке
Рис. 4.7 . Пример CSS Sprites со страницы поиска Google. Источник: www.google.com
Проблемы при загрузке
Проблемы при использовании
Шаг за шагом
Выносим CSS-файлы в пост-загрузку
При использовании data:URI итоговый CSS-файл занимает довольно большой объем (фактически равный 110-120% от размера всех картинок и набору базовых CSS-правил). И это в виде арх
Для решения этой проблемы, во-первых, нам нужно разделить весь массив CSS-правил на относящиеся к фоновым изображениям и не относящиеся. Во-вторых, сообщить браузерам
Фактически, используя такой подход, мы создаем другой контейнер для фоновых изображений (не ресурсное изображение, а CSS-файл), который удобнее использовать в большинстве случаев. Мы объединяем все фо
Теоретическое решение
Все гениальное просто, поэтому мы можем загружать в самом начале страницы достаточно небольшой CSS-файл (без фоновых изображений, только базовые стили, чтобы только
Тут есть и возможные минусы: после загрузки каждого дополнительного CSS-файла будет происходить перерисовка страницы. Однако если таких файлов всего 1 или 2, то отобр
Почему мы не может распараллелить загрузку файлов стилей в самом начале документа? Потому что два файла будут загружаться медленнее, чем один (файлы загружаются по
На практике
На практике все оказалось не сильно сложнее. Мы загружаем в head страницы (до вызовов любых внешних файлов) наш «легкий» CSS:
а затем добавляем в комбинированный обработчик window.onload (подробнее о нем рассказывается в седьмой главе) создание нового файла стилей, который дополняет уже загрузи
В результате мы имеем максимально быстрое отображение страницы, а затем стадию пост-загрузки, которая вытянет с сервера все дополнительные картинки (тут уже сам бр
А доступность?
Внимательные читатели уже заготовили вопрос: а что, если у пользователя отключен JavaScript? Тут всё должно быть просто: мы добавляем соответствующий
После небольших экспериментов было выделено следующее изящное решение, обеспечивающее работу схемы во всех браузерах (замечание: после многочисленных эксперимен
В результате браузер с включенным JavaScript запишет начало комментария, а закроет его только после
Делаем решение кроссбраузерным
В ходе тестирования в Internet Explorer обнаружилось, что если добавлять файл стилей сразу параллельно со скриптами (в функции, которая для него срабатывает по onreadystatechange), т
В Safari же логика отображения страницы в зависимости от загружаемых файлов отличается от всех браузеров. Если в двух словах, то можно жестко определить начальный наб
У Safari второй подход, поэтому ничего лучше выноса загрузки динамического CSS-файла с фоновыми картинками после срабатывания window.onload для этого браузера пока не сущест
Итак, давайте объявим функцию для создания динамического файла стилей:
Выигрыш
При наличии у вас большого количества маленьких декоративных фоновых изображений, которые к тому же могут повторяться по различным направлениям, может быть очень
Описанная техника (кроссбраузерный data:URL плюс динамическая загрузка файлов стилей) позволяет добиться всех преимуществ технологии CSS Sprites, не затягивая загрузку ст
Таким образом, data:URI (в смысле влияния на скорость загрузки) равносильны CSS Sprites (или даже предпочтительнее последней, если учесть, что для повторяющихся и полупрозра
4.6. Методы экстремальной оптимизации
Чем больше число внешних ресурсов, к которым браузер обращается при загрузке, тем больше время требуется для отображения страницы. Как правило, веб-страницы обраща
Объединение JavaScript и CSS в одном файле
Однако существует способ объединения CSS с JavaScript и сведения количества загрузок к одной. Техника основана на том, как CSS и анализатор JavaScript ведут себя в IE и Firefox.
Рассмотрим на примере
Когда анализатор CSS будет разбирать вышеупомянутый код, символы комментария HTML будут пропущены, и код станет эквивалентным следующему примеру:
Анализатор CSS видит только CSS-код, а код скрипта закомментирован (/* ... */).
Когда анализатор JavaScript станет разбирать код, символы комментария HTML будут интерпретированы в комментарии строки (//), и, следовательно, код станет таким:
Анализатор JavaScript видит только код скрипта, а все остальное закомментировано. Чтобы ссылаться на этот ресурс, можно использовать теги
Заметим, что эти два тега ссылаются на один тот же ресурс и, следовательно, он загрузится всего один раз и будет интерпретирован и как стили, и как скрипты.
Есть еще одна вещь, о которой стоит позаботиться, — Content-Type ответа. Его необходимо выставлять в */*, чтобы дать подтверждение Firefox: содержание может быть обработано ка
Указанное решение не работает в Safari (1-5% пользователей), однако конкретно для этого браузера (определив его через User-Agent) уже можно вставить загрузку еще одного файла
Объединение HTML, CSS и JavaScript в одном файле
Чтобы избежать дополнительных запросов со стороны браузера, можно включить непосредственно стилей и(ли) скриптов в сам HTML-документ.
Здесь стоит остановиться на следующем моменте: если размер CSS- (или JavaScript-) файла больше, чем 20% (и при этом больше 5 Кб в сжатом виде), лучше вынести его как отдельный ко
Рассматривать включение всех ресурсов в исходную HTML-страницу стоит только в том случае, если достаточно большой процент посетителей (больше 90%) пришли на нее в перв
Во всех остальных случаях — когда можно выделить достаточно большие ресурсные файлы или когда достаточное количество пользователей приходят не в первый раз — так
Как рабочий пример можно привести заглавные страницы Яндекса и Google — на них вызывается минимум внешних ресурсов, а стилевые правила включены в саму страницу.
Глава 5. Параллельные соединения
5.1. Обходим ограничения браузера на число соединений
Активное ( англ. keep-alive ) соединение стало настоящим прорывом в спецификации HTTP 1.1: оно позволяло использовать уже установленный канал для повторной передачи информации от клиента к серве
Однако HTTP 1.1 добавил веб-разработчикам головной боли по другому поводу. Давайте будем разбираться, как нам устранить и эту проблему.
Издержки на доставку объектов
Средняя веб-страница содержит более 50 объектов, и издержки на число объектов доминируют над всеми остальными задержками при загрузке большинства веб-страниц. Брау
Если число объектов на странице превышает 4, то издержки на ожидание доступных потоков и разбор чанков для присланных объектов превалируют над общим временем загру
При увеличении числа подключаемых объектов сверх 10 время, затрачиваемое на инициализацию соединения, возрастает до 80% и более от общего времени, уходящего на получ
Ограничения спецификации HTTP/1.1
Спецификация HTTP, приблизительно 1999 года, рекомендует, чтобы браузеры и серверы ограничивали число параллельных запросов к одному хосту двумя. Эта спецификация был
Времена меняются
«Режем» соединения
Вы можете, естественно, настроить несколько серверов для обслуживания выдачи картинок или других объектов, чтобы увеличить число параллельных загрузок. Например:
Однако каждый из этих поддоменов не обязан находиться на отдельном сервере.
Реальный выигрыш
Подводим итоги
Сейчас средняя веб-страница состоит более чем из 50 объектов (для Рунета, по статистическим данным
При этом нужно помнить, что увеличение одновременных запросов повлечет задействование дополнительных ресурсов со стороны сервера (это может быть, например, как ма
Стоит коснуться еще одного, весьма интересного момента в оптимизации времени загрузки путем увеличения числа параллельных потоков. Заключается он в выравнивании
Можно пойти и дальше и загружать, например, 4 картинки по 50 Кб в 4 потока, достигая просто феноменального ускорения. Однако тут играет роль психологический фактор: по
Стоит подчеркнуть, что данный подход применим и к другим ресурсным (в том числе и HTML) файлам, однако стоит помнить о весьма жестких ограничениях браузеров на загрузк
5.2. Content Delivery Network и Domain Name System
Подключаем CDN
Yahoo! и Google
Количество DNS-запросов
5.3. Балансировка на стороне клиента
Балансировка нагрузки повышает надежность веб-сайта путем распределения запросов между несколькими (кластером) серверами, если один из них перегружен или отказал
Round-Robin DNS
Популярным, хотя и очень простым подходом для балансировки запросов является циклический DNS. Он подразумевает создание нескольких записей в таблице DNS для одного д
После каждого пользовательского запроса к таблице DNS для www.loadbalancedwebsite.ru, запись, стоящая первой, меняется. Ваш браузер будет использовать первую запись, поэтому все
Можно, конечно, перенести IP-адрес на соседний сервер, который может нести нагрузку. Однако данная процедура весьма хлопотная, чтобы проводить ее в условиях аврала.
Балансировка на сервере
Другим популярным подходом для балансировки запросов является создание одного выделенного сервера, который отвечает за распределение запросов. Примерами таких с
Балансировка на стороне клиента
Существует еще один подход для распределения нагрузки на серверы от современных веб-приложений, который не нуждается в дополнительном балансирующем оборудовании
Вместо того чтобы сказать клиенту, что у нас единственный сервер, можно сообщить о нескольких серверах — s1.loadbalancedsite.ru, s2.loadbalancedsite.ru и так далее. При этом клиентское п
В отличие от веб-приложений, которые хранят код (Javascript или Flash) на одном сервере, обеспечивающем доступ к этой информации, клиентское приложение не зависимо от серве
Рис. 5 .3 . Пример балансировки нагрузки и масштабируемости на клиенте
Итак, можно ли эту технику применить к веб-приложениям? Веб-приложения самой своей сутью размывают границу между клиентской и серверной частями любого стандартног
Сейчас сервер обеспечивает такие ресурсы, как картинки. Но этот факт становится не столь очевидным, если рассмотреть технику CSS Sprites, когда одна картинка является ис
Для обеспечения балансировки на стороне клиента от современного веб-приложения требуется три основных составляющих:
Заметно проще повысить доступность и масштабируемость HTML-кода страниц и других файлов, требуемых на клиенте, чем осуществить то же самое для серверных приложений:
Мы можем включить список доступных серверов в клиентский код точно так же, как сделали бы это для настольного приложения. У веб-приложения доступен файл servers.xml, в ко
Осуществляем кросс-доменные запросы
Даже при небольшом опыте работы с AJAX уже должно было возникнуть закономерное возражение: «Это не будет работать из-за кроссдоменной безопасности» (для предотвраще
Для обеспечения безопасности пользователей веб-браузеры и Flash-клиенты блокируют пользовательские вызовы к другим доменам. Например, если клиентский код хочет обра
Для Flash-клиентов можно просто создать файл crossdomain.xml, который будет разрешать запросы на *.loadbalancedwebsite.ru:
Для клиентского кода на AJAX существуют жесткие ограничения на передачу данных между доменами, которые зависят от методов, используемых для серверных вызовов. Приме
Но что, если на клиенте используется XMLHttpRequest? XHR попросту запрещает клиенту запрашивать отличный от исходного домена сервер. Однако существует небольшая лазейка: е
А если все же AJAX?
Проблема решена.
Преимущества балансировки на стороне клиента
Итак, как только мы обговорили технику, позволяющую осуществлять кроссдоменные вызовы, можно обсудить, собственно, как работает сам балансировщик и как он удовлетв
Подведем итог: каковы же преимущества балансировки на стороне клиента перед балансировкой на стороне сервера? Наиболее очевидное заключается в том, что не требует
Другим преимуществом является то, что все серверы не обязаны быть расположенными в одном месте. Клиент сам выбирает, к какому серверу ему лучше подключиться, в отли
Используем Cloud Computing для балансировки на стороне клиента
В качестве серверной основы приложения можно рассмотреть сервисы Simple Storage Service (S3) и Elastic Computing Cloud (EC2) от
Изначально сервис S3 предоставлял прекрасную возможность для хранения и доставки видеосообщений, а EC2 был спроектирован именно для работы с S3. Он позволяет расширят
Однако большим минусом для EC2 является невозможность проектирования балансировки нагрузки на стороне сервера, у которого не было бы уязвимых мест. Многие веб-прило
Пример приложения
При использовании описанной выше балансировки на стороне клиента становится возможным избежать этого неприятного момента и существенно повысить надежность всег
Чуть раньше указывалось на использование файла servers.xml для оповещения клиента о доступных серверах, но для S3 можно использовать более простой способ. При обращении
Например, по адресу http://s3.amazonaws.com/application/?actions=loadlist будет находиться следующий файл:
В этом примере присутствуют два EC2-сервера в кластере, с IP-адресами 216.255.255.1 и 216.255.255.2 соответственно.
Логика для скрипта, запускающегося по расписанию
Так как скрипт, запускающийся по cron, является частью виртуальной машины EC2, каждая такая машина автоматически регистрируется как доступный сервер в кластере. Клиен
Если виртуальная машина EC2 отказывает или выключается, то другие машины самостоятельно убирают ее запись из сегмента: в сегменте остаются только доступные серверы
5.4. Редиректы, 404-ошибки и повторяющиеся файлы
Редиректы
404- ошибки
5.5. Асинхронные HTTP-запросы
Моделируем параллельные запросы
Рис. 5.4 . Влияние HTTP - конвейера и постоянного соединения на скорость передачи данных. Источник: www.die.net
Предварительные выводы
Рис. 5.5 . Выигрыш при включении постоянного соединения и нескольких хостов для различных пользователей. Источник: www.die.net
Влияние заголовков
Рис. 5.6 . Влияние заголовков на эффективную пропускную способность канала
Давайте рассмотрим сейчас другой вопрос, а именно: как быстро браузер создает DOM-дерево в зависимости от наличия в нем элементов с id или class?
Для этого мы подготовим 3 набора HTML-файлов. Первый будет содержать 10000 элементов, у которых только часть будет иметь id (количество именованных элементов варьируется
Графики влияния DOM-дерева
Ниже приведены разделенные графики по средневзвешенному (естественно, основную роль играет Internet Explorer, ибо сейчас им пользуются от 50% до 70% посетителей наших сайтов)
Рис. 6.1 . Скорость создания документа, средневзвешено по всем браузерам
и график для времени выборки одного элемента из дерева (по идентификатору) при наличии в этом же дереве различного числа элементов с идентификаторами. ID (10000 get) пока
Рис. 6.2 . Скорость выбора элемента, средневзвешено по всем браузерам
Выводы по DOM-дереву
По графику средневзвешенных значений хорошо видно, что при прочих равных условиях создание документа с class обходится меньшей кровью, чем с id (в общем случае от 2% до 1
Для документа со 100 элементами выигрыш может составить почти 1 мс, для документа с 1000 — 8,5 мс! Стоит заметить, что средняя страница в интернете имеет 500–1000 элементов.
Естественно, что приведенные цифры — это уже то, за что можно побороться.
В случае больших веб-приложений задержка в 100 мс (при числе элементов более 10000) уже может оказаться критичной. Ее можно и нужно уменьшать (наряду с другими «узкими» м
Что и требовалось доказать: значительную нагрузку составляет именно создание DOM-дерева в документе. В целом, на эту операцию уходит от 70% всего времени рендеринга (т
На скорость вычисления одного элемента по идентификатору, как ни странно, наибольшее влияние оказывает опять-таки DOM-дерево, а не количество таких элементов. Даже п
Семантическое DOM-дерево
Логическим продолжением уже проведенных исследований CSS/DOM-производительности браузеров стало рассмотрение зависимости времени создания документа от числа тегов
В итоге мы получили примерно следующую картину:
Рис. 6. 3 . Средневзвешенное значение времени создания документа от числа узлов в DOM-дереве
Что быстрее?
Да, очевидно, что размер DOM-дерева влияет на скорость загрузки страницы. Одной из целей данного исследования было показать, как именно влияет (в конкретных числах). С
Различия между линейной и древовидной структурами находятся в пределах погрешности, однако семантическое дерево оказалось самым медленным (чуть ли не на 50%). Но в л
Конечной же целью всех экспериментов было установить, есть ли различие в отображении HTML 4.0 Transitional и XHTML 1.0 Strict документов и какова реальная польза от использования с
Методика для DOCTYPE
Была аккуратно выкачана главная страница Яндекса (она уже хорошо оптимизирована с точки зрения производительности, поэтому проводить эксперименты на ней весьма п
Далее была добавлена стандартная схема измерения загрузки (рендеринга) страницы: время в самом начале head засекается и затем отнимается от времени срабатывания соб
В качестве второй версии страницы бралось приведение ее к валидному XHTML Strict виду. Верстка при этом немного изменилась, но в целом результат получился весьма убедите
Далее в третьей версии уже были убраны все onclick. Больше ничего со страницей не делалось. Ожиданий данная версия не оправдала (только Safari показал значимые отличия от
В четвертом варианте — венце оптимизационных (в отношении CSS/HTML-кода) действий — использование id было сведено к минимуму, все селекторы для class задавались без тегов
Результаты оптимизации
В таблице приведены результаты для основных браузеров (август 2008): размер каждого варианта в байтах и время его загрузки. Времена приведены в миллисекундах.
Size (b)
Gzip (b)
IE6
IE7
IE8b
Firefox 2
Firefox 3
Opera 9.5
Safari 3.1
1
26275
8845
56
80
76
130
127
142
33
2
27173
8993
60
75
320
127
118
148
27
3
26260
8949
61
75
320
131
116
141
23
4
26153
8862
55
73
306
94
102
178
28
«Экономия на спичках»?
В результате тестов удалось показать, что валидный XHTML не медленнее (а даже местами быстрее), чем HTML. И оптимизация реально играет роль (возможно ускорение загрузки H
Естественно, это все «копейки» для обычных пользователей (+/-50 мс —это совершенно не критично). Однако если речь идет про «экономию на спичках», когда нам важен кажды
И что важнее всего, если правильно расставить акценты, то загрузку XHTML можно сделать и быстрее, чем HTML. Различие в размере файлов оказалось в итоге минимальным (26153 пр
В целом в свете тотального распространения мобильных браузеров с их маломощными процессорами такой вид оптимизации выглядит весьма перспективно.
6.4. Ни в коем случае не reflow!
offsetHeight и style.display
IE sp62
IE8b
Firefox 2.0.0.12
Opera 9.22
Safari 3.04b
clean
128
153
15
15
16
offsetHeight
23500
10624
4453
4453
5140
style.display
171
209
56
56
34
height vs. style
140 раз
50 раз
80 раз
80 раз
150 раз
Немного теории
Использование Computed Style
IE sp62
Firefox 2.0.0.12
Opera 9.22
Safari 3.04b
offsetHeight
23500
4453
4453
5140
style.display
171
56
56
34
getStyle
5219
5318
Оптимизация: определение класса hide
IE sp62
Firefox 2.0.0.12
Opera 9.22
Safari 3.04b
offsetHeight
23500
10624
4453
5140
isHidden
231
351
70
71
isHidden2
370
792
212
118
offsetHeight vs. isHidden
102 раза
30 раз
73 раза
92 раза
Заключение
В качестве послесловия: стили или классы?
Метод
IE 6
IE 7
Firefox 1.5
Firefox 2.0
Opera 9
element.className
512
187
291
203
47
element.style.color
1709
422
725
547
282
Перерисовка страницы
Групповое изменение стилей
Два слова о таблицах
Глава 7. Оптимизация JavaScript
7.1. Кроссбраузерный window.onload
Firefox впереди планеты всей
А Internet Explorer?
Условные комментарии
Все так просто?
Двойное выполнение
Избавляемся от внешнего файла
Полное решение
Неблокирующая загрузка JavaScript
Число загрузок с одного хоста
Неблокирующие скрипты
Зависимости
А если по-другому?
Метод
Недостатки
В будущем
7.2. Основы «ненавязчивого» JavaScript
Javascript: храним отдельно
Javascript — это расширение
Доверять, но проверять
Доступ к элементам
Полезные советы
Добавляем обработчики событий
Ускоряем обработку событий
Немного усложним
Боремся с Internet Explorer
Пойдем дальше
Обработка событий в браузерах
Давайте рассмотрим несколько практических способов работы с обработчиками событий в браузерах. Например, можно назначить обработчик напрямую:
Если нужно несколько событий или просто «осторожничаем», то можно воспользоваться следующей распространенной записью:
Или таким модицифицированным вариантом (меньше символов):
Можно также использовать отдельную переменную для обработчика события:
Или записать в одну строку с использованием условной компиляции:
Работаем с событиями
Давайте рассмотрим, что мы можем извлечь из события после перехвата его с помощью соответствующего обработчика:
7.3. Применение «ненавязчивого» JavaScript
В предыдущих разделах были представлены некоторые теоретические аспекты построения клиентской логики, ориентированной на максимальное быстродействие и адекватн
Принципы «ненавязчивой» рекламы
document.write против innerHTML
Контекстная реклама
TopLine, Pop-Up, Pop-Under и RichMedia
Внутренние рекламные сети
Идеальная архитектура рекламной сети
Разгоняем счетчики: от мифов к реальности
Разбираем по косточкам
А если сложнее?
Делаем статистику динамической
7.4. Замыкания и утечки памяти
Шаблоны утечек
Циклические ссылки
Более сложный случай
Замыкания
Постраничные утечки
Псевдо-утечки
Проектируем утечки
7.5. Оптимизируем «тяжелые» JavaScript-вычисления
Оптимизируем вычисления
Улучшаем шаблон
Советы и замечания
Заключение
7.6. Быстрый DOM
DOM DocumentFragment: быстрее быстрого
Нормальное добавление
Добавление при помощи DocumentFragment
Браузер
Нормальный
Fragment
Firefox 3.0.1
90
47
Safari 3.1.2
156
44
Opera 9.51
208
95
IE 6
401
140
IE 7
230
61
IE 8b1
120
40
А если еще быстрее?
innerHTML нам поможет
Его можно значительно ускорить, если добавлять узлы не последовательно один за другим, а сначала создав HTML-строку со всем необходимым кодом, которая будет вставлен
В данном примере кроме уже указанного ускорения еще используется первоначальное создание массива элементов, которые можно объединить через свойство join в строку. Д
7.7. Кэширование в JavaScript
Итерации и локальное кэширование
Кэширование ресурсоемких вызовов
Кэшируем цепочки вызовов
7.8. Быстрые итераторы, регулярные выражения и другие вкусности
Reviews