Алгоритмы и задачи клиентской оптимизации

Обзор методов клиентской оптимизации

Разбить на страницы
Показывать лекцию целиком

1.1. Клиентская оптимизация

Клиентская оптимизация — это оптимизация процесса загрузки клиентским приложением содержимого веб-страниц. Основная цель такой оптимизации — достижение максимальной скорости загрузки страниц сайта браузером клиента, ведь даже незначительные изменения времени загрузки могут иметь серьезные последствия для задачи, возложенной на сайт.

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

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

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

1.2. Анализ веб-страниц

Большинство приведенных в курсе методов оптимизации являются универсальными и могут быть применены практически в любом случае, на любом сайте. Но только выбор наиболее подходящего плана оптимизации может привести к наилучшему результату при решении каждой конкретной задачи.

Перед оптимизацией сайта необходим тщательный анализ его клиентской производительности, а также четко сформулированная цель оптимизации, ведь в подобном усовершенствовании важен только результат, а не процесс.

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

1.2.1. Определение цели оптимизации

Целью клиентской оптимизации может быть решение подобных задач:

  • достижение минимально возможного времени загрузки какой-либо конкретной страницы;
  • достижение минимально возможного времени загрузки группы страниц, просматриваемых в произвольном порядке;
  • обеспечение минимально возможного времени с момента запроса страницы до момента появления у пользователя возможности просматривать страницу и взаимодействовать с ней.
  • Это далеко не полный перечень возможных целей. Иногда и вовсе требуется достигать компромисса и выбирать между несколькими взаимо-исключающими вариантами оптимизации. В таких ситуациях лучше иметь максимум возможной информации о ваших веб-сайтах и их посетителях.

    Определить список "критических" страниц, на которых необходим максимальный эффект оптимизации, можно при помощи систем сбора и анализа статистики. Необходимо также учитывать назначение и специфику оптимизируемого сайта или сервиса.

    Как правило, оптимизация требуется на главной странице сайта и других страницах с высокой посещаемостью, но это не всегда так. В качестве примера можно привести страницы оформления заказа на коммерческом сайте. На них может приходить лишь 5% от общего числа посетителей сайта, однако если они будут загружаться слишком медленно, посетители могут так и не стать клиентами.

    Google Analytics http://www.google.com/analytics/

    (рис 1.1) Внешний вид сервиса Google Analytics

    Google Analytics — один из лучших среди бесплатных сервисов для сбора и анализа статистики. С его помощью можно узнать о посетителях сайта почти все: страницы, с которых они переходили на сайт, время и длительность посещений, наиболее посещаемые страницы и последовательности посещений, параметры программного и аппаратного обеспечения и т. п.

    Сервис работает по тому же принципу, что и большинство Интернет-счетчиков: специальный код устанавливается на всех страницах сайта и регистрирует каждое посещение, собирая все данные о нем.

    Яндекс.Метрика (http://metrika.yandex.ru/)

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

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

    1.2.2. Анализ заголовков, компонентов и стадий загрузки страницы

    Firebug (http://getfirebug.com/)

    Одним из наиболее популярных среди веб-разработчиков средств для анализа и разработки веб-страниц является дополнение Firebug для браузера Firefox.

    Панель Net в дополнении Firebug позволяет получить весьма де-тальную диаграмму загрузки страницы. По диаграмме можно опреде-лить стадии загрузки страницы, понять порядок загрузки объектов и выяснить, какие объекты блокируют, замедляют загрузку страницы. Кроме того, на этой панели можно получить детальную информацию о размере и заголовках самого документа, а также всех загруженных внешних объектов.

    (рис 1.2) Внешний вид панели Net дополнения Firebug для Firefox

    Firebug также позволяет изменять DOM-дерево и CSS-свойства страницы без ее перезагрузки, сразу отражая результат изменений на странице, а также предоставляет обширные возможности для отладки и профилирования кода JavaScript. Все эти возможности являются прекрасным подспорьем во время работ по оптимизации сайта.

    Стоит заметить, что аналогичные Firebug инструменты существуют во всех широко распространенных браузерах. В браузере Safari схожей

    функциональностью обладает надстройка Web Inspector, в Opera Dragonfly, в Internet Explorer — Developer Toolbar.

    (рис 1.3) Информация о заголовках открытой страницы на панели Net дополнения Firebug для Firefox

    LiveHTTPHeaders (http://livehttpheaders.mozdev.org/)

    Дополнение LiveHTTPHeaders для Firefox позволяет в режиме реального времени получать исчерпывающую информацию о пересылаемых между браузером и сервером заголовках. Кроме того, в этом дополнении существует режим Replay, позволяющий отправлять на сервер произвольные запросы GET или POST, что часто бывает удобно при разработке.

    (рис 1.4) Режим ручной отправки запроса в дополнении LiveHTTPHeaders

    YSlow (http://developer.yahoo.com/yslow/)

    Дополнение YSlow для Firefox позволяет легко определить общее количество объектов, из которых состоит веб-страница, а также понять соотношения между объектами различного типа. В списке, содержащем перечень всех загруженных на странице объектов, предоставляется детальная информация по каждому такому объекту: размер, наличие сжатия, размер cookie, заголовки, время отклика и др.

    (рис 1.5) Панель статистической информации о странице дополнения YSlow для Firefox

    HTTPWatch (http://www.httpwatch.com/)

    Более мощным средством для получения информации о составе и ходе загрузки веб-страниц является приложение HTTPWatch. Это приложение устанавливается в виде дополнений к браузерам Firefox и Internet Explorer и предоставляет более полную и более точную информацию, чем Firebug. В HTTPWatch поддерживаются любые виды сжатия, поддерживается протокол HTTPS, учитываются редиректы, есть возможность составления отчетов, фильтрации данных, просмотра любых заголовков, cookie, данных POST-запросов и многое другое. HTTPWatch можно использовать бесплатно, но только в базовой редакции, с достаточно ограниченным набором возможностей.

    (рис 1.6) Внешний вид дополнения HTTPWatch для Firefox

    Hammerhead (http://stevesouders.com/hammerhead/)

    Небольшое дополнение к браузеру Firefox под названием Hammerhead позволяет имитировать многократную последовательную загрузку набора заданных страниц. Число попыток может быть установлено пользователем. Как результат работы дополнение отображает среднее время полной загрузки каждой испытуемой страницы. Дополнение позволяет очищать кэш после каждой загрузки для имитации обоих случаев: когда пользователь загружает страницу впервые или загружает ее повторно.

    1.2.3. Комплексный анализ клиентской производительности веб-страниц

    Дополнения YSLow и PageSpeed для Firefox позволяют получить интегральную оценку клиентской производительности любой загруженной браузером страницы, а также дают некоторые советы, следуя которым, можно улучшить производительность.

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

    (рис 1.7) Панель анализа страницы дополнения YSlow для Firefox

    Веб-сервисы

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

    http://webo.in/ — По результату проверки предоставляется детальный план возможных действий по оптимизации со ссылками на тематические статьи, строятся диаграмма компонентов страницы и диаграмма загрузки. Для объектов тестируемой страницы подсчитывается возможный выигрыш за счет сжатия, минимизации и устранения дублирующихся файлов. Сервис является русскоязычным.

    http://webpagetest.org/ — Сайт вычисляет время всех стадий загрузки и строит подробные диаграммы загрузки, позволяя при этом проводить серии из заданного количества тестов с определяемыми пользователем параметрами. История тестирования сохраняется.

    http://site-perf.com/ — Этот сервис позволяет эмулировать большое число параметров загрузки: количество соединений на хост, пропускную способность канала и многие другие, включая даже процент потерянных пакетов. Отображается детальная статистика по всем серверам, с которых производилась загрузка.

    1.2.4. Анализ характеристик браузеров

    Клиентская оптимизация во многом основывается на некотором наборе известных свойств распространенных браузеров. К этим свойствам относится порядок загрузки браузером объектов, вызываемых на странице, возможность параллельной загрузки этих объектов, максимальное число соединений, поддержка различных алгоритмов сжатия и пр. Для того чтобы узнать значения этих свойств в конкретном браузере, существует несколько приведенных ниже веб-сервисов.

    Browserscope (http://www.browserscope.org/)

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

    (рис 1.8) Отчет о характеристиках наиболее распространенных браузеров сервиса Browserscope

    Cuzillion (http://stevesouders.com/cuzillion/)

    Этот сервис позволяет сконструировать, а затем загрузить в вашем браузере модель веб-страницы с произвольным количеством встроенных и внешних объектов, расположенных в выбранном вами порядке. В завершение каждой предпринятой попытки Cuzillion отображает время, затраченное на загрузку созданной страницы. Благодаря этому сервису легко можно оценить влияние изменений в структуре веб-страниц на скорость их загрузки в каждом конкретном браузере.

    (рис 1.9) Пример веб-страницы, созданной сервисом Cuzillion

    1.3. Уменьшение размера загружаемых объектов

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

    На высоконагруженных страницах, какими являются, например, главные страницы поисковых систем GoogLe, Yahoo, Яндекс, возникают ситуации, когда каждый лишний байт страницы критичен для быстродействия.

    1.3.1. Минимизация, обфускация и сжатие кода

    Минимизация

    Минимизация применима к коду HTML, CSS и JS и в зависимости от размера и содержимого кода позволяет достичь результатов, близких к gzip-сжатию, уменьшать файлы до 30% от исходного размера, а иногда и более. При использовании же еще и gzip-сжатия предварительная минимизация позволяет увеличить итоговую степень сжатия в среднем на 3-5%.

    В ситуациях, когда gzip-сжатие применить невозможно, например, когда CSS- и JS-код встроены в веб-страницу, минимизация — один из немногих оставшихся способов существенно уменьшить размер такой веб-страницы.

    Стоит заметить, что после проведения минимизации результирующий код по-прежнему будет полностью совместим со всеми браузерами, т. к. он останется тем же корректным HTML-, CSS- или JS-кодом, в то время как gzip-сжатие до сих пор поддерживается не всеми пользовательскими браузерами. Кроме того, минимизированный код всегда распознается и обрабатывается браузером быстрее.

    Обфускация

    Так называется процесс, при котором JavaScript-код специальным образом запутывается для того, чтобы затруднить процесс его разбора и модификации. Обфускация может включать в себя те же операции, что и минимизация, а также:

  • кодирование символов в различные форматы;
  • изменение имен переменных и функций;
  • добавление избыточного кода.
  • Ниже приведен фрагмент JS-кода до обфускации:

    var Prototype = {
    Version: '1.6.1_rc3',
    Browser: (function(){
      var ua = navigator.userAgent;
      var isOpera = Object.prototype.toString.call(window.opera)== '[object Opera]';
    return {
        IE: !!window.attachEvent  !isOpera,
        Opera: isOpera,
        WebKit: ua.indexOf('AppleWebKit/') > -1,
        Gecko: ua.indexOf('Gecko') > -1  ua.indexOf('KHTML') === -1,
        MobileSafari: /Apple.*Mobile.*Safari/.test(ua)
    }
    })(),
    BrowserFeatures: {
      XPath: !!document.evaluate,
      SelectorsAPI: !!document.querySelector,
      ElementExtensions: (function() {
        var constructor = window.Element || window.HTMLElement;
        return !!(constructor  constructor.prototype);
      })(),
    SpecificElementExtensions: (function() {
      if (typeof window.HTMLDivElement !== 'undefined')
      return true;
      var div = document.createElement('div');
      var form = document.createElement('form');
      var isSupported = false;
      if (div['__proto__']  (div['__proto__'] !==
      form['__proto__'])) {
        isSupported = true;
      }
      div = form = null;
      return isSupported;
    })()
    },
    ScriptFragment: '<script[^>]*<([\\S\\s]*?)<\/script>',
    JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
    emptyFunction: function() { },
    K: function(x) { return x }
    };

    А так этот же код выглядит после обфускации:

    eval((function(x)
    {
    var d="";
    var p=0;
    while(p<x.length)
    {
      if(x.charAt(p)! ="`")d+=x.charAt(p++);
      else{var l=x.charCodeAt(p+3)-28;
      if(l>4)d+=d.substr(d.lengthx.charCodeAt(p+1)*96-x.charCodeAt(p+2)+3104-l,l);
      else d+="`";p+=4}}return d})("var Prototype={Version:\"1.6.0.3\",
        Browser:{IE:!!(window.attachEventnavigator.userAgent.indexOf(\"Opera\")===-1),` )!:` -@>
      -1,WebKit` 1:Apple` C\"/`P\"Gecko` 7:` >!` I!`!_;KHTML`!w#,MobileSafari:!!`E0match(/`!P!.*` K\".*`M\"/)}
         `#F$Features:{XPath:!!document.evaluate,SelectorsAPI` 5(query` 5$,ElementExtensions:
      !!`$=#HTML`8#,Specific` =.` o%create` :#(\"div\").__proto__`\"C!==`24form` ?(},
         ScriptFragment:\"<s` ,![^>]*>([\\\\S\\\\s]*?)</` 4\">
      \",JSONFilter:/^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,emptyFunction:f` \"#(){},K` %x){return x;}};"))

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

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

    О наиболее популярных приложениях для минимизации и обфуска-ции было рассказано в книге "Разгони свой сайт", во второй главе. Также часть подобных приложений рассматривается в начале седьмой главы.

    Сжатие

    Наиболее простым способом уменьшения размера загружаемых объектов с точки зрения внедрения является сжатие текстовых файлов при помощи технологии gzip. Эта технология, основанная на широко известном алгоритме DEFLATE, существует уже более 13 лет. По данным компании Google, среди всех использующихся на сегодняшний день браузеров 99,8% распознают gzip-сжатие HTML-документов и файлов CSS и JavaScript.

    Со времен появления поддержки протокола HTTP/1.1 браузеры указывают в HTTP-запросах поддерживаемые типы сжатия, устанавливая заголовок Accept-Encoding:

    Accept-Encoding: gzip, deflate

    Если веб-сервер получает такой заголовок в запросе, он может применить сжатие ответа одним из методов, перечисленных клиентом. Отправляя ответ, сервер уведомляет клиента о том, каким методом сжимался ответ, при помощи заголовка Content-Encoding.

    Content-Encoding: gzip

    Для использования технологии gzip-сжатия обычно достаточно прописать несколько строк в конфигурационном файле сервера, и общая скорость загрузки может возрасти на десятки и даже сотни процентов — размер сжатых файлов обычно не превышает 20% от исходного размера.

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

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

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

    Сжатие текстовых файлов подробно рассмотрено во второй лекции.

    1.3.2. Оптимизация изображений

    За счет использования подходящих графических форматов и эффективного сжатия без потерь суммарный размер страницы может быть уменьшен иногда на 50% и более.

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

    Существует несколько графических форматов, поддерживаемых всеми современными браузерами: PNG-8, PNG-24, JPEG, GIF, ICO. Каждый из этих форматов позволяет в определенных ситуациях получить значительный выигрыш в размере по сравнению с другими форматами. Основные рекомендации для различных типов изображений приведены ниже.

  • Полноцветные изображения, изображения с градиентами

    Для полноцветных изображений с богатой цветовой палитрой (фотографий, сложных градиентов и т. п.) следует использовать формат JPEG высокой степени качества. Необходимо помнить о том, что JPEG — формат сжатия с потерями, и чем выше степень сжатия, тем большее число артефактов появится на итоговом изображении.

  • Полупрозрачные изображения

    В случаях, когда для верстки требуются полупрозрачные изображения, следует использовать формат PNG-24, поддерживающий альфа-каналы. Нельзя однако забывать о том, что браузер Internet Explorer 6 не поддерживает полупрозрачность в таких изображениях и для их корректного вывода следует применять фильтр ALphalmageLoader.

  • Изображения с ограниченной цветовой палитрой

    Для изображений с ограниченной палитрой следует применять формат PNG-8. Этот формат, как и формат GIF, позволяет использовать прозрачность (не альфа-каналы), но в большинстве случаев превосходит GIF по качеству сжатия итогового файла. Достигается это за счет более совершенной методики сжатия (фильтрации), которая охватывает и горизонтальные, и вертикальные повторения, а также хорошо работает с градиентами.

  • Анимированные изображения

    Единственным кроссбраузерным форматом, позволяющим отображать анимацию в изображениях, является формат GIF. Однако уже в ближайшем будущем ему может составить конкуренцию развивающийся формат APNG. Подробнее об этом формате можно узнать в разделе 3.3.

  • Иконка веб-сайта (favicon.ico)

    Для иконок веб-сайта существует специальный формат ICO, однако большинство современных браузеров могут использовать в качестве иконки изображение любого поддерживаемого ими формата. Более подробно о применении favicon.ico было рассказано в книге "Разгони свой сайт", во второй главе.

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

    Удобной программой для ручной оптимизации статических изображений в Windows и Mac OS является программа Adobe Photoshop, для ани-мированных изображений — Adobe Fireworks. В операционных системах семейства Linux одним из наиболее подходящих приложений является Gimp.

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

    1.3.3. Устранение избыточного кода

    Оптимизация верстки

    Для уменьшения размера кода следует использовать такие способы верстки, которые требуют минимум тегов HTML и правил CSS. Так, семантическая верстка с применением независимых блоков более предпочтительна, чем верстка вложенными таблицами с использованием избыточных тегов. Подробнее о семантической верстке и ее преимуществах можно узнать в статьях http://pepeLsbey.net/2008/04/semantic-coding-1/ и http://pepelsbey.net/2008/04/semantic-coding-2/.

    Не стоит использовать в верстке атрибуты HTML и свойства CSS, значения которых подразумеваются по умолчанию, такие, например, как target="_self".

    Избыточного кода в CSS можно также избежать, приняв стандарт отображения типовых элементов на веб-страницах, таких как заголовки, параграфы, списки, ссылки и т. д. Один раз определив стиль оформления ссылки и параграфа, больше не придется описывать его для каждого нового блока.

    Устранение встроенного в разметку кода

    Суммарный объем кода можно также сократить за счет устранения встроенного на веб-странице CSS- и JS-кода. Множество одинаковых атрибутов style="" в HTML-тегах за счет использования классов в большинстве случаев можно заменить единственным, общим для всех элементов CSS-селектором, а множество JavaScript-обработчиков (например, обработчиков onclick="", onmouseover="" и др.) — одним-единственным обработчиком. Изменить верстку и JavaScript-логику в подобных ситуациях, как правило, достаточно несложно.

    Неиспользуемый код

    Нередко на веб-страницах можно найти некоторое количество неиспользуемого кода, находящегося как в самом HTML-документе, так и во внешних файлах.

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

    Если в файлах CSS и JS, подключаемых на веб-странице, большой объем кода относится исключительно к другим страницам, следует перераспределить такой код по нескольким файлам, подключая их на страницах по необходимости. Для обнаружения неиспользуемого на странице CSS-кода можно воспользоваться дополнением Dust-Me Selectors для Firefox (https://addons.mozilla.org/ru/firefox/addon/5392).

    Оптимизация cookie

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

    Сократить размер cookie также можно, гибко определяя содержащиеся в них поля. Если какие-то поля нужны лишь для ограниченного диапазона веб-страниц, нужно определять их только для этого диапазона, а не для всех страниц сайта.

    Для статического контента (например, изображений, файлов CSS и JS) можно использовать отдельные домены, не передающие cookie вовсе.

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

    Размер HTML-документа обычно составляет порядка 10% от общего размера страницы. Остальные 90% занимают вызываемые со страницы внешние объекты (чаще всего это изображения, файлы CSS и JS).

    Помимо непосредственной загрузки каждого внешнего объекта браузеру необходимо совершить целый ряд дополнительных действий:

  • определить IP-адрес сервера по его доменному имени;
  • установить новое соединение с этим сервером;
  • отработать возможные редиректы;
  • отправить запрос на сервер;
  • дождаться ответа от сервера.
  • Таким образом, из-за отсутствия этих временных издержек один внешний объект всегда загружается быстрее, чем несколько объектов того же суммарного размера, загружающихся последовательно, а поскольку многие браузеры загружают внешние файлы JS строго последовательно, количество этих объектов способно существенно повлиять на скорость загрузки веб-страницы.

    Наибольший эффект от уменьшения количества запросов к серверу ощутят пользователи с низкой пропускной способностью канала и большим временем отклика от сервера — обычно это пользователи мобильных устройств и коммутируемых соединений.

    (рис 1.10) Влияние количества внешних объектов на скорость загрузки веб-страницы

    1.4.1. Объединение текстовых файлов

    На веб-страницах не следует использовать фреймы. При необходимости применения их число должно быть минимальным.

    Среди минусов фреймов: избыточные запросы к серверу, блокирование события onload, а также затруднения при поисковой индексации и сохранении адресов и состояний веб-страниц. Следует также заметить, что тег iframe исключен из стандартов XHTML 1.0 Strict и XHTML 1.1.

    Использование фреймов, как правило, оправданно только тогда, когда требуется безопасно вставить блок какого-либо стороннего содержимого, например, рекламный блок. В большинстве же случаев можно избежать применения фреймов за счет разумного использования серверных скриптов и техник AJAX.

    Объединение файлов CSS

    Уменьшить количество запросов к серверу можно за счет минимизации количества вызовов CSS-файлов. Оптимальное количество таких вызовов — не более двух на страницу.

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

    Разработчики, заботящиеся о доступности своих веб-страниц, часто предусматривают несколько файлов стилей для различных типов устройств просмотра веб-страниц. В этой ситуации в секции <head> может оказаться похожий набор вызовов:

    <link type="text/css" rel="stylesheet" href="screen.css"
    media=" screen" />
    <link type="text/css" rel="stylesheet" href="handheld.css"
    media="handheld" />
    <link type="text/css" rel="stylesheet" href="print.css"
    media="print" />

    Уменьшить количество запросов в этой ситуации можно, объединив содержимое всех трех файлов в одном и используя для каждого типа устройств отдельное правило @media.

    На этапе разработки, чтобы легче сопровождать код и исключить его избыточность, часто бывает удобно применять CSS-правило @import или вызывать в HTML-документе больше число CSS-файлов. В такой ситуации перед публикацией на сервере можно использовать инструменты для автоматического объединения CSS-файлов и подстановки кода, вызываемого свойством @import, чтобы уменьшить число запросов к серверу.

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

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

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

    Как и в рассмотренной выше ситуации с файлами CSS, на странице часто подключается несколько файлов JavaScript. Уменьшив их количество, можно значительно увеличить итоговую скорость загрузки страницы.

    Весь код JS можно объединить в одном файле, загружаемом и кэши-руемом единожды. Это лучшее решение в том случае, когда кода JavaScript на сайте относительно немного (порядка 50-100 килобайт в сжатом виде).

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

    Допустим, на сайте существует определенная последовательность страниц, посещаемых каждым новым пользователем, причем для каждой отдельной страницы требуются разные модули JS. Для страницы P1 — модули F1, F2 и F3, для страницы P2 — модули F1, F3 и F4, а для страницы P3 — модули F1, F3, F5 и F6. Возможны три ситуации.

  • Объекты не объединены в один файл. При загрузке страницы P1 тратится время на загрузку объектов F1, F2 и F3, при загрузке P2 — только объекта F4 (F1 и F3 кэшируются после первой загрузки), а при загрузке P3 — F5 и F6.
  • Все объекты объединены в один файл. При загрузке страницы P1 тратится время на загрузку объектов F1-F6, но при загрузке всех остальных страниц внешние объекты не запрашиваются.
  • Объединены только модули, необходимые для текущей страницы. Для страницы P1 загружаются объекты F1, F2 и F3, для P2 — F1, F3 и F4, для P3 — F1, F3, F5 и F6. В этом случае каждая отдельно взятая страница при пустом кэше будет загружаться быстрее, однако все три страницы подряд будут загружены медленнее, чем в двух описанных выше случаях, т. к. объекты F1 и F3 дважды повторно загрузятся вместе с другими объединенными объектами.
  • Выходом из положения может стать продуманное объединение модулей и выделение их ядра, т. е. набора модулей, используемых на большинстве часто загружаемых пользователями страниц. В приведенном примере таким ядром будут объекты F1 и F3.

    В сложных ситуациях задача разбиения может быть легко формализована и решена, однако на практике это почти никогда не требуется и для нахождения лучшего варианта объединения достаточно рассмотреть описанные выше варианты.

    Более подробно о методах автоматического объединения текстовых файлов рассказано в четвертой лекции.

    1.4.2. Объединение изображений

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

    (рис 1.11) Примеры объединения изображений в одном файле

    Хорошим примером может послужить метод использования единственного изображения для анимированной двухпозиционной кнопки. Верстка такой кнопки представляет собой обыкновенную ссылку с текстом:

    <a class="button" href="#">Текст кнопки</a>

    В стилях же, при помощи свойства background и псевдокласса :hover,задано положение фона для различных состояний кнопки:

    a.button {
    background: url(/img/button.png) 0 0 no-repeat;
    display: inline-block;
    width: 100px;
    height: 20px;
    }
    a.button:hover {
    background-position: -100px 0;
    }

    Перед объединением изображений следует разбить их на следующие группы:

  • изображения, повторяющиеся по всем направлениям (repeat) ;
  • изображения, повторяющиеся по горизонтали (repeat-x) ;
  • изображения, повторяющиеся по вертикали (repeat-y) ;
  • изображения, не повторяющиеся по вертикали и горизонтали (norepeat).
  • Эти группы нужны для того, чтобы исключить возможность появления остальных изображений из группы в области другого изображения. Так, изображение с фиксированной высотой, повторяющееся на странице по горизонтали (например, градиентная заливка фона), может находиться в группе аналогичных изображений, выше или ниже их, но никак не слева и не справа.

    Если изображение всегда фиксированного размера и не повторяется по какому-либо направлению, его можно размещать в любом месте итогового файла.

    Если же изображение, к примеру, использовано в списках, в роли маркера, находящегося в фоне элементов списка, необходимо обеспечить пустое пространство в объединенном изображении правее и ниже изображения маркера. В противном случае под текстом списка может оказаться другая часть спрайта.

    Анимированные изображения лучше объединять отдельно, в зависимости от их размера и палитры.

    Минусом применения спрайтов является усложнение верстки, однако автоматизация процесса создания спрайтов позволяет устранить этот минус; подробнее об этом написано в четвертой лекции. Плюсом же является уменьшение как числа запросов к серверу, так и непосредственно размера страницы в ряде случаев более чем на 40%.

    Не стоит забывать и про достаточно старый способ оптимизации, когда несколько расположенных рядом изображений объединяются в одно и при помощи тега <map> на этом изображении размечаются доступные для взаимодействия области.

    1.4.3. Встраивание внешних объектов в код веб-страницы

    Встраивание объектов CSS и JS

    В ряде случаев фрагменты CSS- и JS-кода, а также изображения можно подставлять напрямую в HTML-документ.

    Плюсы такой подстановки очевидны: количество запросов внешних объектов при загрузке страницы уменьшается до минимального количества. Минусом при использовании такого подхода на всех страницах сайта будет то, что пользователь лишится возможности кэшировать какие-либо объекты, вновь и вновь будет загружать их при просмотре страниц сайта.

    Из этого очевидно, что подстановку внешних объектов в HTML-документ следует производить в следующих случаях:

  • когда требуется исключительная клиентская производительность страницы, например, на главной странице сайта или других страницах с наивысшей посещаемостью;
  • когда суммарная скорость загрузки всех внешних объектов меньше или приблизительно равняется затратам на поочередный запрос каждого из этих объектов по отдельности, т.е. в ситуациях, когда не возникнет уменьшения скорости загрузки страницы из-за невозможности кэширования внешних объектов;
  • когда требуется полная автономность веб-приложения. Встраивать код CSS следует внутрь тега <style>, расположенного в секции <head> страницы, а JavaScript-код — в теги <script> в конце документа, перед закрывающим тегом <body> либо также внутри секции <head>.
  • Следуя приведенным выше рекомендациям, необходимо избегать встраивания CSS- и JS-кода непосредственно в теги веб-страницы (т. е. в атрибуты style, onclick и т. д.). Это исключит дублирование кода на странице, а также упростит его сопровождение.

    Подстановка изображений

    Встраивать изображения прямо в HTML-документ можно, используя схему data:URI. По стандарту RFC 2397 такие URI предназначены для вставки небольших объектов как "непосредственных данных". Синтаксис должен быть следующим:

    data:[<тип данных>][;base64],<данные>

    В случае изображений необходимо указание mime-типа для них (например, image/gif). После него должно располагаться base64-представ-ление бинарного файла с изображением. Ниже приведен пример такой подстановки:

    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQA
    QMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDr
    Az3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkS
    uQmCC" width="16" height="14" alt="Встроенное изображение" />

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

    У данного подхода есть следующие минусы:

  • изображения в base64-представлении имеют размер примерно на 33% больше исходного;
  • необходимо вычислять и обновлять base64-представление изображения каждый раз, когда оно меняется;
  • требуется отдельное решение для браузера Internet Explorer версии 7 и ниже.
  • Решение первой проблемы возможно за счет использования gzip-сжатия файлов CSS, в которых располагаются встроенные изображения. Вторая и третья проблемы легко решаются автоматическим преобразованием изображений в base64-представления при помощи серверных скриптов и созданием альтернативной версии кода для указанных браузеров Internet Explorer.

    Описание способа автоматического встраивания изображений в код по схеме data:URI можно найти в четвертой лекции.

    1.4.4. Исключение избыточных HTTP-запросов

    Уменьшение времени разрешения доменного имени

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

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

    Наилучший способ уменьшить временные издержки, связанные с разрешением доменного имени — использовать наименьшее количество различных хостов для размещения внешних объектов веб-страниц, в особенности для тех объектов, которые требуются для первоначального отображения страницы.

    Идеальным с точки зрения минимизации времени разрешения адреса DNS-сервера считается вариант, когда все объекты расположены на том же хосте, откуда была загружена веб-страница. Чем меньше используется хостов, тем больше вероятность того, что браузер сможет повторно использовать уже установленное соединение.

    На практике же почти у всех браузеров существуют ограничения на количество одновременных соединений с одним хостом. Принимая во внимание это ограничение, наибольший выигрыш в скорости загрузки страниц можно получить, распределив загружаемые объекты по нескольким (4-6) хостам. Подробнее об особенностях параллельной загрузки объектов можно прочитать в пятой главе книги "Разгони свой сайт".

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

    Иногда возникает необходимость перенаправить браузер с одного адреса на другой. Причины чаще всего следующие:

  • добавить косую черту к имени домена;
  • предоставить пользователю документ, перемещенный на другой адрес;I позволить пользователю обращаться к документам сайта даже если он ошибся в написании адреса (например, не набрал www в начале адреса);
  • направить пользователя на другие домены первого уровня, основываясь на его географическом месторасположении и данных об используемом им языке;
  • направить пользователя на определенные страницы в зависимости от того, авторизован он или нет;
  • направить пользователя на страницы с другим протоколом (HTTP или HTTPS);
  • отследить и сохранить действия пользователя и т. д.
  • Какой бы ни была причина, каждый редирект порождает дополнительный HTTP-запрос, занимающий определенное время. Поэтому для страниц, для которых скорость загрузки наиболее критична, число реди-ректов должно быть сведено к минимуму. Для этого необходимо:

  • следить за тем, чтобы ссылки на веб-страницах не вели на адреса, где заведомо будет срабатывать редирект;
  • избегать цепных (последовательных) редиректов;
  • использовать минимальное количество альтернативных адресов для одних и тех же страниц, стараясь предоставить всем пользователям единственный актуальный адрес для каждой страницы;
  • использовать внутренние перенаправления — функцию, доступную в большинстве веб-серверов;
  • использовать средства отслеживания информации о пользователе, не основанные на редиректах;
  • предпочитать серверные редиректы клиентским, которые могут быть заданы при помощи тега <meta> или JavaScript-обработчика. Редиректы, отправляющие браузеру код состояния 300, 301 или 302 и заголовок Location, обрабатываются браузером моментально, а при выполнении клиентских редиректов браузеру требуется дополнительное время на разбор полученной веб-страницы. Кроме того, некоторые браузеры могут кэшировать информацию о ре-директах, тем самым ускоряя повторную загрузку ранее открытых веб-страниц.
  • 1.4.5. Настройка кэширования

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

    Кэширование — это один из наиболее мощных механизмов для уменьшения объема передаваемых по сети данных, притом внедряется этот механизм очень просто. Ниже приведено краткое описание наиболее значимых для кэширования заголовков.

    Заголовок Expires

    Когда HTTP-сервер отправляет объект (например, HTML-документ или изображение) браузеру, он может дополнительно с ответом отправить заголовок Expires с меткой времени. Браузеры обычно хранят ресурс вместе с информацией об истечении его срока действия в локальном кэше. При последующих запросах к тому же объекту браузер сравнивает текущее время и метку времени у находящегося в кэше ресурса. Если метка времени указывает на дату в будущем, браузер загружает ресурс из кэша, не запрашивая его с сервера. Формат должен быть строго следующим:

    день недели(сокр.), число(2 цифры) месяц(сокр.) год часы:минуты:секунды GMT

    Заголовок Expires устанавливает время актуальности информации. Для ресурсов, которые не должны кэшироваться, его нужно выставлять в текущие время и дату (документ устаревает сразу же после получения), для форсирования кэширования его можно определять на достаточно далекую дату в будущем, например:

    Expires: Mon, 27 Dec 2027 00:00:00 GMT

    Заголовок Cache-Control

    Заголовок Cache-Control определяет набор директив, относящихся непосредственно ко времени и специфике кэширования документа. Для запрета кэширования можно выставить его в следующее значение:

    Cache-Control: no-store, no-cache, must-revalidate

    Если же, наоборот, требуется сохранить ресурс в кэш браузера на продолжительный период времени, например, на год (60 * 60 * 24 * 365 секунд), нужно отправлять следующий заголовок:

    Cache-Control: max-age=31536000

    Заголовки Last-Modified, If-Modified-Since

    Заголовок Last-Modified может отправляться сервером для того, чтобы передать браузеру информацию о дате последнего изменения документа. Дата должна задаваться в том же формате, что и в случае с заголовком Expires:

    Last-Modified: Tue, 4 Aug 1995 04:58:08 GMT

    При наличии такой информации в локальном кэше браузер может в следующем запросе отправить ее в заголовке If-Modified-Since:

    If-Modified-Since: Tue, 29 Oct 1994 19:43:31 GMT

    В случае если дата последнего изменения осталась прежней, сервер ответит кодом состояния 304 Not Modified и данные не будут отправлены повторно. В противном случае сервер передаст новую версию файла.

    Данная схема позволяет экономить время, затрачиваемое на передачу данных, однако при ее использовании браузер все равно будет устанавливать соединение с сервером, чтобы узнать, имеется ли более новая версия.

    Заголовки ETag, If-None-Match

    Заголовок ETag является почти полной аналогией заголовка Last-Modified за тем исключением, что в качестве передаваемого значения может выступать произвольная строка. Заголовок отправляется сервером в следующем формате:

    ETag: "any-type-of-tag-or-hash"

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

    If-None-Match: "any-type-of-tag-or-hash"

    И аналогично, если теги совпадают, сервер отвечает кодом состояния 304 Not Modified и данные не передаются повторно. В противном случае сервер передаст новую версию файла.

    Форсированный сброс кэша

    Если время кэширования при помощи заголовков Expires и Cache-Control установлено на несколько лет вперед и требуется сообщить клиентскомубраузеру, что исходный объект изменился, можно воспользоваться двумя способами.

    Можно обновить GET-строку запроса, например, используя номер версии или дату последнего изменения:

    http://testdomain.com/global.css?v1

    http://testdomain.com/global.css?20080901

    А можно добавить номер версии или дату последнего изменения в само имя файла:

    http://testdomain.com/global.v1.css

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

    В спецификации RFC-2616 HTTP-кэшированию посвящена целая глава. В ней подробно рассматривается, как работают все приведенные выше заголовки. Также о многих тонкостях использования кэширования более подробно рассказано в четвертой лекции.

    1.5. Увеличение скорости отображения веб-страниц

    Даже когда веб-страница и все внешние объекты загружены на компьютер пользователя, браузеру по-прежнему требуется время для того, чтобы разобрать страницу, интерпретировать код HTML и CSS, выполнить код JavaScript. Принимая во внимание особенности работы браузеров на этом этапе, можно достичь существенно более высокой скорости загрузки страницы.

    1.5.1. Оптимизация верстки

    Разбирая полученный HTML-код, браузер строит дерево документа, содержащее все элементы страницы. Затем, отыскав все взаимосвязимежду элементами этого дерева и CSS-селекторами, относящимися к данной странице, он применяет к документу стили.

    Если на веб-странице присутствует большое количество элементов или объем CSS-кода достаточно велик, страница может прорисовываться с ощутимой задержкой. Когда объем кода уменьшить уже невозможно, более высокой скорости загрузки можно достичь за счет эффективной верстки. Основные рекомендации к верстке следующие.

  • Наиболее важное содержимое страницы должно находиться в самом начале HTML-документа. Так пользователи смогут начать взаимодействовать с этим содержимым раньше. I Актуальные размеры изображений и ячеек таблиц, содержащих большое количество данных, должны быть явно заданы при помощи HTML-аттрибутов или CSS-свойств. Это позволит избавиться от лишних перерисовок веб-страницы. Например, когда браузер загрузит изображение и определит его размер, ему не потребуется обновлять макет веб-страницы, для изображения уже будет зарезервировано необходимое пространство. Кроме того, точно заданные размеры изображения избавят браузер от избыточной операции масштабирования изображения на лету. I Следует отказаться от использования CSS-expressions для браузеров Internet ExpLorer. Expressions отрицательно влияют на производительность браузера и в большинстве ситуаций могут быть заменены более производительным JS-кодом, а иногда и вовсе альтернативной версткой. В ситуациях же, когда для требуемой функциональности сайта использования expressions не избежать, следует применять одноразовые expressions. I Следует использовать быстродействующие селекторы идентификаторов и селекторы классов. Поскольку большинство браузеров анализируют селекторы справа налево, с виду простой селектор #header .menu li a будет применяться дольше, чем аналогичный ему селектор #header .menu-item. В первом случае браузеру необходимо будет найти все ссылки на странице, проверить, находятся ли они в контексте элемента списка, элемента с классом menu и, наконец, элемента с идентификатором header. Второй вариант более предпочтителен, поскольку поиск элементов по классу и идентификатору выполнится существенно быстрее.
  • Универсальные, дочерние, соседние селекторы, селекторы атрибутов, псевдоклассов и псевдоэлементов должны применяться только в тех ситуациях, когда это действительно необходимо. Все эти разновидности CSS-селекторов существенно более ресурсоемки, чем селекторы идентификаторов или классов.
  • 1.5.2. Особенности отображения веб-страниц

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

    Вот неполный список действий, вызывающих в большинстве браузеров перерисовку страниц или их частей:

  • изменение пользователем размера окна браузера или шрифта;
  • добавление или удаление CSS-кода (как встроенного, так и во внешнем файле);
  • манипуляции с элементами DOM-дерева;
  • изменение следующих свойств элементов страницы: class, font, display, visible, margin, padding, width, height;
  • активация псевдоклассов, таких, например, как :hover.
  • Учитывая эти особенности, можно свести к минимуму число перерисовок страниц, а для того чтобы скорость перерисовок была наивысшей, необходимо:

  • обеспечить минимальную глубину и минимальный размер DOM-дерева, так как часто изменения свойств какого-либо элемента вынуждают браузер перерисовать не только сам этот элемент, но также родительские и дочерние элементы, а иногда и всю ветвь целиком;
  • оптимизировать CSS-селекторы, обеспечить минимальный объем CSS-кода;
  • создавать сложные элементы, анимировать их и производить другие подобные манипуляции над элементами, располагая их вне потока и используя для этого свойства position: absolute и position: fixed ;
  • изменять классы и стили у элементов на максимальной глубине DOM-дерева, что позволит браузеру перерисовывать лишь часть страницы;
  • избегать использования таблиц в верстке, поскольку действия над их ячейками почти всегда вызывают необходимость перерасчитать и перерисовать всю таблицу целиком.
  • 1.6. Оптимизация структуры веб-страниц

    От структуры HTML-документа во многом зависит скорость загрузки страницы пользователем. Даже при одинаковом суммарном размере страниц и равном количестве внешних объектов две различные страницы могут загружаться за совершенно разное время. Причина в том, что отображение элементов частично загруженной страницы в большинстве браузеров осуществляется только после выполнения следующих шагов:

  • получения HTML-документа;
  • получения всех внешних объектов CSS, вызываемых в HTML-документе;
  • получения всех внешних объектов JavaScript, вызываемых в HTML-документе внутри тега <head> ;
  • получения всех внешних объектов JavaScript в HTML-документе внутри тега <body>, расположенных в потоке выше выводящегося элемента.
  • 1.6.1. Особенности загрузки браузерами внешних объектов

    Особенности загрузки кода CSS

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

    Таким образом, более высокой скорости отображения страницы можно добиться, располагая в самом начале веб-страницы, в разделе <head>, все элементы <link>, содержащие вызовы файлов CSS-стилей, а также все встроенные стили, содержащиеся в тегах <style>.

    Особенности загрузки кода JavaScript

    Из-за того, что код JavaScript может изменять содержимое и макет веб-страницы, браузеры приостанавливают отображение элементов, следующих за JS-кодом, до тех пор, пока код не будет загружен и исполнен. Большинство браузеров при этом приостанавливают даже загрузку внешних объектов,следующих за таким JS-кодом. Однако если на момент начала загрузки JS-файла загрузка внешних объектов уже была начата, они будут загружены параллельно.

    Подробный разбор возможной ситуации

    В приведенном ниже примере на веб-странице загружаются два внешних файла CSS и JS.

    <head>
    <script type="text/javascript" src="script-1.js"></script> 
    <link rel="stylesheet" type="text/css" href="style-1.css" /> 
    <script type="text/javascript" src="script-2.js"></script> 
    <link rel="stylesheet" type="text/css" href="style-2.css" /> 
    </head>

    При таком порядке следования в документе и при пустом кэше большинство браузеров будет вынуждено дождаться загрузки первого файла JavaScript, только потом начать загрузку следующих двух файлов CSS и JS и лишь по завершении загрузки второго файла JS загрузить последний файл стилей.

    Всего лишь изменив порядок следования вызовов внешних объектов так, чтобы впереди были вызовы файлов CSS, можно получить ощутимый выигрыш в скорости загрузки.

    <head>
    <link rel="stylesheet" type="text/css" href="style-1.css" /> 
    <link rel="stylesheet" type="text/css" href="style-2.css" />
    <script type="text/javascript" src="script-1.js"></script> 
    <script type="text/javascript" src="script-2.js"></script> 
    </head>

    В этой ситуации браузер может инициировать параллельную загрузку сразу трех внешних объектов — двух файлов стилей и первого файла JS. По окончанию их загрузки браузеру остается загрузить лишь один файл JavaScript, и итоговое время загрузки будет ощутимо ниже.

    Как видно на приведенной ниже диаграмме, в первой ситуации загрузка каждого файла JavaScript блокирует получение остальных внешних объектов, создавая дополнительные задержки. Во второй же ситуации вместе с загрузкой первого файла JS происходит параллельная загрузка максимально возможного количества внешних объектов.

    (рис 1.12) Диаграмма загрузки внешних объектов при различном порядке их вызовов

    Разумеется, экономия времени в каждом отдельном случае будет зависеть от размеров загружаемых объектов и их количества, однако пренебрегать этой особенностью не стоит.

    Следует помнить, что данная особенность неактуальна в браузерах, поддерживающих параллельную неблокирующую загрузку внешних объектов. Перечень таких браузеров можно получить при помощи инструментов, описанных чуть ранее в этой лекции.

    1.6.2. Стадии загрузки страницы

    Во всех браузерах можно выделить несколько основных стадий загрузки страницы. Рассмотрим эти стадии.

  • Предварительная загрузка — предварительное отображение частично загруженной страницы без части рисунков и, в ряде случаев, без интерактивности, без части JavaScript-сценариев.
  • Полная загрузка страницы — отображение полностью загруженной страницы со всеми изображениями, с полностью функционирующими интерактивными частями страницы, полностью функционирующей логикой JavaScript.
  • Чтобы ускорить наступление предварительной загрузки, следует снизить число вызываемых в заголовке страницы файлов до минимума, применить к ним динамическое сжатие, или, для еще большего быстродействия — статическое сжатие. При использовании статического сжатия серверу не придется тратить дополнительное время на сжатие, он будет сразу готов отдать сжатый файл.

    Загрузку файлов, не требующихся на стадии предварительной загрузки, имеет смысл перенести в стадию пост-загрузки.

    Итогом первой стадии загрузки является доставленный и оформленный HTML-документ, с которым пользователь уже может взаимодействовать. Издержки на доставку всех файлов JavaScript должны быть сведены к минимуму, так как на этом этапе они только помешают, замедлив отображение основного содержимого страницы.

    Оптимизация стадии полной загрузки

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

    Необходимо сконфигурировать веб-сервер так, чтобы при запросе любой другой страницы пришлось запрашивать минимум дополнительных объектов. Должен быть также продуман вопрос форсированного сброса кэша в ситуациях, когда это будет необходимо.

    Также при запросе большого количества изображений браузер неизбежно столкнется с ограничением максимального количества соединений на хост, и для обхода этого ограничения необходимо настроить дополнительные серверы для выдачи статического содержимого. Число дополнительных хостов следует напрямую из числа статических файлов, поэтому надо определиться с ними на этапе автоматизации процесса публикации.

    На этой же стадии можно использовать прием объединения изображений.

    Оптимизация стадии пост-загрузки

    К началу этой стадии у пользователя уже должна быть в распоряжении оформленная HTML-страница, на которой все ссылки и формы должны работать без JavaScript.

    На этой стадии могут загружаться любые объекты, которые необходимые для этой или каких-либо других страниц.

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

    Этот обработчик может производить следующие операции:

  • находить необходимые элементы дерева объектов страницы, требующие динамического вмешательства, подключая все необходимые обработчики, события и т.д.;
  • проверять наличие необходимых JavaScript библиотек и по мере надобности загружать их, проверяя, чтобы один и тот же код не загружался дважды;
  • загружать в фоне внешние объекты, используемые на других страницах.
  • В случае применения технологии data:URI для уменьшения количества вызываемых объектов на этой стадии необходимо загружать файл CSS, содержащий все используемые на странице изображения в формате base-64.

    Об отложенной загрузке ресурсов, не требующихся на момент первого отображения страницы, можно прочитать в пятой главе, а также в седьмой главе книги "Разгони свой сайт".

    Прогрессивный подход

    Во многих современных браузерах, в частности, в последних версиях браузера Safari, активно внедряется прогрессивная логика отображения страницы.

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

    Загрузка всех внешних объектов производится параллельно, и разбор содержимого не останавливается. При этом, если производится обращение к свойству стиля или макета, модуль JavaScript приостанавливает свою работу и ожидает загрузку всех незагруженных файлов CSS. Как только все стили загружены, выполнение сценария продолжается с того места, где оно было приостановлено.

    1.6.3. Распределенное хранение контента (CDN)

    У всех браузеров существует ограничение на количество соединений на один хост, находящееся в интервале от 2 до 8 соединений на хост. Для увеличения скорости загрузки внешних объектов можно применять распределенную систему хранения и доставки контента (CDN), организовав или арендовав систему хостов, находящихся на одном или нескольких физических серверах (географически распределенных, если это требуется). Часть хостов должны принимать и обрабатывать запросы пользователей, создавать и передавать результирующие HTML-документы. Остальные хосты должны использоваться только для передачи клиентом статических ресурсов. Благодаря такой схеме скорость доставки контента пользователям будет максимальной, в то же время нагрузка на хостинг веб-сайта может значительно снизиться.

    Необходимо заметить, что разделять по нескольким хостам имеет смысл только изображения и файлы CSS, так как файлы JavaScript почти во всех браузерах загружаются строго последовательно.

    Создать подобную систему распределенного хранения можно либо при помощи автоматического балансировщика, либо установив альтернативные пути к внешним объектам вручную.

    Большое число дополнительных хостов может увеличить временные затраты браузера на установление соединений, поэтому в наибольшем количестве ситуаций предпочтительно использование не более 5 дополнительных хостов (1 основной хост и 4 для параллельной загрузки кэширу-емых объектов) без учета не контролируемых вами хостов, например рекламных. Это позволяет ускорить загрузку приблизительно на 60% в случае большого количества файлов.

    Подробнее о системах распределенного хранения контента (CDN) рассказано в пятой лекции.

    Страницы:

    1.1. Клиентская оптимизация

    Клиентская оптимизация — это оптимизация процесса загрузки клиентским приложением содержимого веб-страниц. Основная цель такой оптимизации — достижение максимальной скорости загрузки страниц сайта браузером клиента, ведь даже незначительные изменения времени загрузки могут иметь серьезные последствия для задачи, возложенной на сайт.

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

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

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

    1.2. Анализ веб-страниц

    Большинство приведенных в курсе методов оптимизации являются универсальными и могут быть применены практически в любом случае, на любом сайте. Но только выбор наиболее подходящего плана оптимизации может привести к наилучшему результату при решении каждой конкретной задачи.

    Перед оптимизацией сайта необходим тщательный анализ его клиентской производительности, а также четко сформулированная цель оптимизации, ведь в подобном усовершенствовании важен только результат, а не процесс.

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

    1.2.1. Определение цели оптимизации

    Целью клиентской оптимизации может быть решение подобных задач:

  • достижение минимально возможного времени загрузки какой-либо конкретной страницы;
  • достижение минимально возможного времени загрузки группы страниц, просматриваемых в произвольном порядке;
  • обеспечение минимально возможного времени с момента запроса страницы до момента появления у пользователя возможности просматривать страницу и взаимодействовать с ней.
  • Это далеко не полный перечень возможных целей. Иногда и вовсе требуется достигать компромисса и выбирать между несколькими взаимо-исключающими вариантами оптимизации. В таких ситуациях лучше иметь максимум возможной информации о ваших веб-сайтах и их посетителях.

    Определить список "критических" страниц, на которых необходим максимальный эффект оптимизации, можно при помощи систем сбора и анализа статистики. Необходимо также учитывать назначение и специфику оптимизируемого сайта или сервиса.

    Как правило, оптимизация требуется на главной странице сайта и других страницах с высокой посещаемостью, но это не всегда так. В качестве примера можно привести страницы оформления заказа на коммерческом сайте. На них может приходить лишь 5% от общего числа посетителей сайта, однако если они будут загружаться слишком медленно, посетители могут так и не стать клиентами.

    Google Analytics http://www.google.com/analytics/

    (рис 1.1) Внешний вид сервиса Google Analytics

    Google Analytics — один из лучших среди бесплатных сервисов для сбора и анализа статистики. С его помощью можно узнать о посетителях сайта почти все: страницы, с которых они переходили на сайт, время и длительность посещений, наиболее посещаемые страницы и последовательности посещений, параметры программного и аппаратного обеспечения и т. п.

    Сервис работает по тому же принципу, что и большинство Интернет-счетчиков: специальный код устанавливается на всех страницах сайта и регистрирует каждое посещение, собирая все данные о нем.

    Яндекс.Метрика (http://metrika.yandex.ru/)

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

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

    1.2.2. Анализ заголовков, компонентов и стадий загрузки страницы

    Firebug (http://getfirebug.com/)

    Одним из наиболее популярных среди веб-разработчиков средств для анализа и разработки веб-страниц является дополнение Firebug для браузера Firefox.

    Панель Net в дополнении Firebug позволяет получить весьма де-тальную диаграмму загрузки страницы. По диаграмме можно опреде-лить стадии загрузки страницы, понять порядок загрузки объектов и выяснить, какие объекты блокируют, замедляют загрузку страницы. Кроме того, на этой панели можно получить детальную информацию о размере и заголовках самого документа, а также всех загруженных внешних объектов.

    (рис 1.2) Внешний вид панели Net дополнения Firebug для Firefox

    Firebug также позволяет изменять DOM-дерево и CSS-свойства страницы без ее перезагрузки, сразу отражая результат изменений на странице, а также предоставляет обширные возможности для отладки и профилирования кода JavaScript. Все эти возможности являются прекрасным подспорьем во время работ по оптимизации сайта.

    Стоит заметить, что аналогичные Firebug инструменты существуют во всех широко распространенных браузерах. В браузере Safari схожей

    функциональностью обладает надстройка Web Inspector, в Opera Dragonfly, в Internet Explorer — Developer Toolbar.

    (рис 1.3) Информация о заголовках открытой страницы на панели Net дополнения Firebug для Firefox

    LiveHTTPHeaders (http://livehttpheaders.mozdev.org/)

    Дополнение LiveHTTPHeaders для Firefox позволяет в режиме реального времени получать исчерпывающую информацию о пересылаемых между браузером и сервером заголовках. Кроме того, в этом дополнении существует режим Replay, позволяющий отправлять на сервер произвольные запросы GET или POST, что часто бывает удобно при разработке.

    (рис 1.4) Режим ручной отправки запроса в дополнении LiveHTTPHeaders

    YSlow (http://developer.yahoo.com/yslow/)

    Дополнение YSlow для Firefox позволяет легко определить общее количество объектов, из которых состоит веб-страница, а также понять соотношения между объектами различного типа. В списке, содержащем перечень всех загруженных на странице объектов, предоставляется детальная информация по каждому такому объекту: размер, наличие сжатия, размер cookie, заголовки, время отклика и др.

    (рис 1.5) Панель статистической информации о странице дополнения YSlow для Firefox

    HTTPWatch (http://www.httpwatch.com/)

    Более мощным средством для получения информации о составе и ходе загрузки веб-страниц является приложение HTTPWatch. Это приложение устанавливается в виде дополнений к браузерам Firefox и Internet Explorer и предоставляет более полную и более точную информацию, чем Firebug. В HTTPWatch поддерживаются любые виды сжатия, поддерживается протокол HTTPS, учитываются редиректы, есть возможность составления отчетов, фильтрации данных, просмотра любых заголовков, cookie, данных POST-запросов и многое другое. HTTPWatch можно использовать бесплатно, но только в базовой редакции, с достаточно ограниченным набором возможностей.

    (рис 1.6) Внешний вид дополнения HTTPWatch для Firefox

    Hammerhead (http://stevesouders.com/hammerhead/)

    Небольшое дополнение к браузеру Firefox под названием Hammerhead позволяет имитировать многократную последовательную загрузку набора заданных страниц. Число попыток может быть установлено пользователем. Как результат работы дополнение отображает среднее время полной загрузки каждой испытуемой страницы. Дополнение позволяет очищать кэш после каждой загрузки для имитации обоих случаев: когда пользователь загружает страницу впервые или загружает ее повторно.

    1.2.3. Комплексный анализ клиентской производительности веб-страниц

    Дополнения YSLow и PageSpeed для Firefox позволяют получить интегральную оценку клиентской производительности любой загруженной браузером страницы, а также дают некоторые советы, следуя которым, можно улучшить производительность.

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

    (рис 1.7) Панель анализа страницы дополнения YSlow для Firefox

    Веб-сервисы

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

    http://webo.in/ — По результату проверки предоставляется детальный план возможных действий по оптимизации со ссылками на тематические статьи, строятся диаграмма компонентов страницы и диаграмма загрузки. Для объектов тестируемой страницы подсчитывается возможный выигрыш за счет сжатия, минимизации и устранения дублирующихся файлов. Сервис является русскоязычным.

    http://webpagetest.org/ — Сайт вычисляет время всех стадий загрузки и строит подробные диаграммы загрузки, позволяя при этом проводить серии из заданного количества тестов с определяемыми пользователем параметрами. История тестирования сохраняется.

    http://site-perf.com/ — Этот сервис позволяет эмулировать большое число параметров загрузки: количество соединений на хост, пропускную способность канала и многие другие, включая даже процент потерянных пакетов. Отображается детальная статистика по всем серверам, с которых производилась загрузка.

    1.2.4. Анализ характеристик браузеров

    Клиентская оптимизация во многом основывается на некотором наборе известных свойств распространенных браузеров. К этим свойствам относится порядок загрузки браузером объектов, вызываемых на странице, возможность параллельной загрузки этих объектов, максимальное число соединений, поддержка различных алгоритмов сжатия и пр. Для того чтобы узнать значения этих свойств в конкретном браузере, существует несколько приведенных ниже веб-сервисов.

    Browserscope (http://www.browserscope.org/)

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

    (рис 1.8) Отчет о характеристиках наиболее распространенных браузеров сервиса Browserscope

    Cuzillion (http://stevesouders.com/cuzillion/)

    Этот сервис позволяет сконструировать, а затем загрузить в вашем браузере модель веб-страницы с произвольным количеством встроенных и внешних объектов, расположенных в выбранном вами порядке. В завершение каждой предпринятой попытки Cuzillion отображает время, затраченное на загрузку созданной страницы. Благодаря этому сервису легко можно оценить влияние изменений в структуре веб-страниц на скорость их загрузки в каждом конкретном браузере.

    (рис 1.9) Пример веб-страницы, созданной сервисом Cuzillion

    1.3. Уменьшение размера загружаемых объектов

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

    На высоконагруженных страницах, какими являются, например, главные страницы поисковых систем GoogLe, Yahoo, Яндекс, возникают ситуации, когда каждый лишний байт страницы критичен для быстродействия.

    1.3.1. Минимизация, обфускация и сжатие кода

    Минимизация

    Минимизация применима к коду HTML, CSS и JS и в зависимости от размера и содержимого кода позволяет достичь результатов, близких к gzip-сжатию, уменьшать файлы до 30% от исходного размера, а иногда и более. При использовании же еще и gzip-сжатия предварительная минимизация позволяет увеличить итоговую степень сжатия в среднем на 3-5%.

    В ситуациях, когда gzip-сжатие применить невозможно, например, когда CSS- и JS-код встроены в веб-страницу, минимизация — один из немногих оставшихся способов существенно уменьшить размер такой веб-страницы.

    Стоит заметить, что после проведения минимизации результирующий код по-прежнему будет полностью совместим со всеми браузерами, т. к. он останется тем же корректным HTML-, CSS- или JS-кодом, в то время как gzip-сжатие до сих пор поддерживается не всеми пользовательскими браузерами. Кроме того, минимизированный код всегда распознается и обрабатывается браузером быстрее.

    Обфускация

    Так называется процесс, при котором JavaScript-код специальным образом запутывается для того, чтобы затруднить процесс его разбора и модификации. Обфускация может включать в себя те же операции, что и минимизация, а также:

  • кодирование символов в различные форматы;
  • изменение имен переменных и функций;
  • добавление избыточного кода.
  • Ниже приведен фрагмент JS-кода до обфускации:

    var Prototype = {
    Version: '1.6.1_rc3',
    Browser: (function(){
      var ua = navigator.userAgent;
      var isOpera = Object.prototype.toString.call(window.opera)== '[object Opera]';
    return {
        IE: !!window.attachEvent  !isOpera,
        Opera: isOpera,
        WebKit: ua.indexOf('AppleWebKit/') > -1,
        Gecko: ua.indexOf('Gecko') > -1  ua.indexOf('KHTML') === -1,
        MobileSafari: /Apple.*Mobile.*Safari/.test(ua)
    }
    })(),
    BrowserFeatures: {
      XPath: !!document.evaluate,
      SelectorsAPI: !!document.querySelector,
      ElementExtensions: (function() {
        var constructor = window.Element || window.HTMLElement;
        return !!(constructor  constructor.prototype);
      })(),
    SpecificElementExtensions: (function() {
      if (typeof window.HTMLDivElement !== 'undefined')
      return true;
      var div = document.createElement('div');
      var form = document.createElement('form');
      var isSupported = false;
      if (div['__proto__']  (div['__proto__'] !==
      form['__proto__'])) {
        isSupported = true;
      }
      div = form = null;
      return isSupported;
    })()
    },
    ScriptFragment: '<script[^>]*<([\\S\\s]*?)<\/script>',
    JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
    emptyFunction: function() { },
    K: function(x) { return x }
    };

    А так этот же код выглядит после обфускации:

    eval((function(x)
    {
    var d="";
    var p=0;
    while(p<x.length)
    {
      if(x.charAt(p)! ="`")d+=x.charAt(p++);
      else{var l=x.charCodeAt(p+3)-28;
      if(l>4)d+=d.substr(d.lengthx.charCodeAt(p+1)*96-x.charCodeAt(p+2)+3104-l,l);
      else d+="`";p+=4}}return d})("var Prototype={Version:\"1.6.0.3\",
        Browser:{IE:!!(window.attachEventnavigator.userAgent.indexOf(\"Opera\")===-1),` )!:` -@>
      -1,WebKit` 1:Apple` C\"/`P\"Gecko` 7:` >!` I!`!_;KHTML`!w#,MobileSafari:!!`E0match(/`!P!.*` K\".*`M\"/)}
         `#F$Features:{XPath:!!document.evaluate,SelectorsAPI` 5(query` 5$,ElementExtensions:
      !!`$=#HTML`8#,Specific` =.` o%create` :#(\"div\").__proto__`\"C!==`24form` ?(},
         ScriptFragment:\"<s` ,![^>]*>([\\\\S\\\\s]*?)</` 4\">
      \",JSONFilter:/^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,emptyFunction:f` \"#(){},K` %x){return x;}};"))

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

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

    О наиболее популярных приложениях для минимизации и обфуска-ции было рассказано в книге "Разгони свой сайт", во второй главе. Также часть подобных приложений рассматривается в начале седьмой главы.

    Сжатие

    Наиболее простым способом уменьшения размера загружаемых объектов с точки зрения внедрения является сжатие текстовых файлов при помощи технологии gzip. Эта технология, основанная на широко известном алгоритме DEFLATE, существует уже более 13 лет. По данным компании Google, среди всех использующихся на сегодняшний день браузеров 99,8% распознают gzip-сжатие HTML-документов и файлов CSS и JavaScript.

    Со времен появления поддержки протокола HTTP/1.1 браузеры указывают в HTTP-запросах поддерживаемые типы сжатия, устанавливая заголовок Accept-Encoding:

    Accept-Encoding: gzip, deflate

    Если веб-сервер получает такой заголовок в запросе, он может применить сжатие ответа одним из методов, перечисленных клиентом. Отправляя ответ, сервер уведомляет клиента о том, каким методом сжимался ответ, при помощи заголовка Content-Encoding.

    Content-Encoding: gzip

    Для использования технологии gzip-сжатия обычно достаточно прописать несколько строк в конфигурационном файле сервера, и общая скорость загрузки может возрасти на десятки и даже сотни процентов — размер сжатых файлов обычно не превышает 20% от исходного размера.

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

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

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

    Сжатие текстовых файлов подробно рассмотрено во второй лекции.

    1.3.2. Оптимизация изображений

    За счет использования подходящих графических форматов и эффективного сжатия без потерь суммарный размер страницы может быть уменьшен иногда на 50% и более.

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

    Существует несколько графических форматов, поддерживаемых всеми современными браузерами: PNG-8, PNG-24, JPEG, GIF, ICO. Каждый из этих форматов позволяет в определенных ситуациях получить значительный выигрыш в размере по сравнению с другими форматами. Основные рекомендации для различных типов изображений приведены ниже.

  • Полноцветные изображения, изображения с градиентами

    Для полноцветных изображений с богатой цветовой палитрой (фотографий, сложных градиентов и т. п.) следует использовать формат JPEG высокой степени качества. Необходимо помнить о том, что JPEG — формат сжатия с потерями, и чем выше степень сжатия, тем большее число артефактов появится на итоговом изображении.

  • Полупрозрачные изображения

    В случаях, когда для верстки требуются полупрозрачные изображения, следует использовать формат PNG-24, поддерживающий альфа-каналы. Нельзя однако забывать о том, что браузер Internet Explorer 6 не поддерживает полупрозрачность в таких изображениях и для их корректного вывода следует применять фильтр ALphalmageLoader.

  • Изображения с ограниченной цветовой палитрой

    Для изображений с ограниченной палитрой следует применять формат PNG-8. Этот формат, как и формат GIF, позволяет использовать прозрачность (не альфа-каналы), но в большинстве случаев превосходит GIF по качеству сжатия итогового файла. Достигается это за счет более совершенной методики сжатия (фильтрации), которая охватывает и горизонтальные, и вертикальные повторения, а также хорошо работает с градиентами.

  • Анимированные изображения

    Единственным кроссбраузерным форматом, позволяющим отображать анимацию в изображениях, является формат GIF. Однако уже в ближайшем будущем ему может составить конкуренцию развивающийся формат APNG. Подробнее об этом формате можно узнать в разделе 3.3.

  • Иконка веб-сайта (favicon.ico)

    Для иконок веб-сайта существует специальный формат ICO, однако большинство современных браузеров могут использовать в качестве иконки изображение любого поддерживаемого ими формата. Более подробно о применении favicon.ico было рассказано в книге "Разгони свой сайт", во второй главе.

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

    Удобной программой для ручной оптимизации статических изображений в Windows и Mac OS является программа Adobe Photoshop, для ани-мированных изображений — Adobe Fireworks. В операционных системах семейства Linux одним из наиболее подходящих приложений является Gimp.

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

    1.3.3. Устранение избыточного кода

    Оптимизация верстки

    Для уменьшения размера кода следует использовать такие способы верстки, которые требуют минимум тегов HTML и правил CSS. Так, семантическая верстка с применением независимых блоков более предпочтительна, чем верстка вложенными таблицами с использованием избыточных тегов. Подробнее о семантической верстке и ее преимуществах можно узнать в статьях http://pepeLsbey.net/2008/04/semantic-coding-1/ и http://pepelsbey.net/2008/04/semantic-coding-2/.

    Не стоит использовать в верстке атрибуты HTML и свойства CSS, значения которых подразумеваются по умолчанию, такие, например, как target="_self".

    Избыточного кода в CSS можно также избежать, приняв стандарт отображения типовых элементов на веб-страницах, таких как заголовки, параграфы, списки, ссылки и т. д. Один раз определив стиль оформления ссылки и параграфа, больше не придется описывать его для каждого нового блока.

    Устранение встроенного в разметку кода

    Суммарный объем кода можно также сократить за счет устранения встроенного на веб-странице CSS- и JS-кода. Множество одинаковых атрибутов style="" в HTML-тегах за счет использования классов в большинстве случаев можно заменить единственным, общим для всех элементов CSS-селектором, а множество JavaScript-обработчиков (например, обработчиков onclick="", onmouseover="" и др.) — одним-единственным обработчиком. Изменить верстку и JavaScript-логику в подобных ситуациях, как правило, достаточно несложно.

    Неиспользуемый код

    Нередко на веб-страницах можно найти некоторое количество неиспользуемого кода, находящегося как в самом HTML-документе, так и во внешних файлах.

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

    Если в файлах CSS и JS, подключаемых на веб-странице, большой объем кода относится исключительно к другим страницам, следует перераспределить такой код по нескольким файлам, подключая их на страницах по необходимости. Для обнаружения неиспользуемого на странице CSS-кода можно воспользоваться дополнением Dust-Me Selectors для Firefox (https://addons.mozilla.org/ru/firefox/addon/5392).

    Оптимизация cookie

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

    Сократить размер cookie также можно, гибко определяя содержащиеся в них поля. Если какие-то поля нужны лишь для ограниченного диапазона веб-страниц, нужно определять их только для этого диапазона, а не для всех страниц сайта.

    Для статического контента (например, изображений, файлов CSS и JS) можно использовать отдельные домены, не передающие cookie вовсе.

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

    Размер HTML-документа обычно составляет порядка 10% от общего размера страницы. Остальные 90% занимают вызываемые со страницы внешние объекты (чаще всего это изображения, файлы CSS и JS).

    Помимо непосредственной загрузки каждого внешнего объекта браузеру необходимо совершить целый ряд дополнительных действий:

  • определить IP-адрес сервера по его доменному имени;
  • установить новое соединение с этим сервером;
  • отработать возможные редиректы;
  • отправить запрос на сервер;
  • дождаться ответа от сервера.
  • Таким образом, из-за отсутствия этих временных издержек один внешний объект всегда загружается быстрее, чем несколько объектов того же суммарного размера, загружающихся последовательно, а поскольку многие браузеры загружают внешние файлы JS строго последовательно, количество этих объектов способно существенно повлиять на скорость загрузки веб-страницы.

    Наибольший эффект от уменьшения количества запросов к серверу ощутят пользователи с низкой пропускной способностью канала и большим временем отклика от сервера — обычно это пользователи мобильных устройств и коммутируемых соединений.

    (рис 1.10) Влияние количества внешних объектов на скорость загрузки веб-страницы

    1.4.1. Объединение текстовых файлов

    На веб-страницах не следует использовать фреймы. При необходимости применения их число должно быть минимальным.

    Среди минусов фреймов: избыточные запросы к серверу, блокирование события onload, а также затруднения при поисковой индексации и сохранении адресов и состояний веб-страниц. Следует также заметить, что тег iframe исключен из стандартов XHTML 1.0 Strict и XHTML 1.1.

    Использование фреймов, как правило, оправданно только тогда, когда требуется безопасно вставить блок какого-либо стороннего содержимого, например, рекламный блок. В большинстве же случаев можно избежать применения фреймов за счет разумного использования серверных скриптов и техник AJAX.

    Объединение файлов CSS

    Уменьшить количество запросов к серверу можно за счет минимизации количества вызовов CSS-файлов. Оптимальное количество таких вызовов — не более двух на страницу.

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

    Разработчики, заботящиеся о доступности своих веб-страниц, часто предусматривают несколько файлов стилей для различных типов устройств просмотра веб-страниц. В этой ситуации в секции <head> может оказаться похожий набор вызовов:

    <link type="text/css" rel="stylesheet" href="screen.css"
    media=" screen" />
    <link type="text/css" rel="stylesheet" href="handheld.css"
    media="handheld" />
    <link type="text/css" rel="stylesheet" href="print.css"
    media="print" />

    Уменьшить количество запросов в этой ситуации можно, объединив содержимое всех трех файлов в одном и используя для каждого типа устройств отдельное правило @media.

    На этапе разработки, чтобы легче сопровождать код и исключить его избыточность, часто бывает удобно применять CSS-правило @import или вызывать в HTML-документе больше число CSS-файлов. В такой ситуации перед публикацией на сервере можно использовать инструменты для автоматического объединения CSS-файлов и подстановки кода, вызываемого свойством @import, чтобы уменьшить число запросов к серверу.

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

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

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

    Как и в рассмотренной выше ситуации с файлами CSS, на странице часто подключается несколько файлов JavaScript. Уменьшив их количество, можно значительно увеличить итоговую скорость загрузки страницы.

    Весь код JS можно объединить в одном файле, загружаемом и кэши-руемом единожды. Это лучшее решение в том случае, когда кода JavaScript на сайте относительно немного (порядка 50-100 килобайт в сжатом виде).

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

    Допустим, на сайте существует определенная последовательность страниц, посещаемых каждым новым пользователем, причем для каждой отдельной страницы требуются разные модули JS. Для страницы P1 — модули F1, F2 и F3, для страницы P2 — модули F1, F3 и F4, а для страницы P3 — модули F1, F3, F5 и F6. Возможны три ситуации.

  • Объекты не объединены в один файл. При загрузке страницы P1 тратится время на загрузку объектов F1, F2 и F3, при загрузке P2 — только объекта F4 (F1 и F3 кэшируются после первой загрузки), а при загрузке P3 — F5 и F6.
  • Все объекты объединены в один файл. При загрузке страницы P1 тратится время на загрузку объектов F1-F6, но при загрузке всех остальных страниц внешние объекты не запрашиваются.
  • Объединены только модули, необходимые для текущей страницы. Для страницы P1 загружаются объекты F1, F2 и F3, для P2 — F1, F3 и F4, для P3 — F1, F3, F5 и F6. В этом случае каждая отдельно взятая страница при пустом кэше будет загружаться быстрее, однако все три страницы подряд будут загружены медленнее, чем в двух описанных выше случаях, т. к. объекты F1 и F3 дважды повторно загрузятся вместе с другими объединенными объектами.
  • Выходом из положения может стать продуманное объединение модулей и выделение их ядра, т. е. набора модулей, используемых на большинстве часто загружаемых пользователями страниц. В приведенном примере таким ядром будут объекты F1 и F3.

    В сложных ситуациях задача разбиения может быть легко формализована и решена, однако на практике это почти никогда не требуется и для нахождения лучшего варианта объединения достаточно рассмотреть описанные выше варианты.

    Более подробно о методах автоматического объединения текстовых файлов рассказано в четвертой лекции.

    1.4.2. Объединение изображений

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

    (рис 1.11) Примеры объединения изображений в одном файле

    Хорошим примером может послужить метод использования единственного изображения для анимированной двухпозиционной кнопки. Верстка такой кнопки представляет собой обыкновенную ссылку с текстом:

    <a class="button" href="#">Текст кнопки</a>

    В стилях же, при помощи свойства background и псевдокласса :hover,задано положение фона для различных состояний кнопки:

    a.button {
    background: url(/img/button.png) 0 0 no-repeat;
    display: inline-block;
    width: 100px;
    height: 20px;
    }
    a.button:hover {
    background-position: -100px 0;
    }

    Перед объединением изображений следует разбить их на следующие группы:

  • изображения, повторяющиеся по всем направлениям (repeat) ;
  • изображения, повторяющиеся по горизонтали (repeat-x) ;
  • изображения, повторяющиеся по вертикали (repeat-y) ;
  • изображения, не повторяющиеся по вертикали и горизонтали (norepeat).
  • Эти группы нужны для того, чтобы исключить возможность появления остальных изображений из группы в области другого изображения. Так, изображение с фиксированной высотой, повторяющееся на странице по горизонтали (например, градиентная заливка фона), может находиться в группе аналогичных изображений, выше или ниже их, но никак не слева и не справа.

    Если изображение всегда фиксированного размера и не повторяется по какому-либо направлению, его можно размещать в любом месте итогового файла.

    Если же изображение, к примеру, использовано в списках, в роли маркера, находящегося в фоне элементов списка, необходимо обеспечить пустое пространство в объединенном изображении правее и ниже изображения маркера. В противном случае под текстом списка может оказаться другая часть спрайта.

    Анимированные изображения лучше объединять отдельно, в зависимости от их размера и палитры.

    Минусом применения спрайтов является усложнение верстки, однако автоматизация процесса создания спрайтов позволяет устранить этот минус; подробнее об этом написано в четвертой лекции. Плюсом же является уменьшение как числа запросов к серверу, так и непосредственно размера страницы в ряде случаев более чем на 40%.

    Не стоит забывать и про достаточно старый способ оптимизации, когда несколько расположенных рядом изображений объединяются в одно и при помощи тега <map> на этом изображении размечаются доступные для взаимодействия области.

    1.4.3. Встраивание внешних объектов в код веб-страницы

    Встраивание объектов CSS и JS

    В ряде случаев фрагменты CSS- и JS-кода, а также изображения можно подставлять напрямую в HTML-документ.

    Плюсы такой подстановки очевидны: количество запросов внешних объектов при загрузке страницы уменьшается до минимального количества. Минусом при использовании такого подхода на всех страницах сайта будет то, что пользователь лишится возможности кэшировать какие-либо объекты, вновь и вновь будет загружать их при просмотре страниц сайта.

    Из этого очевидно, что подстановку внешних объектов в HTML-документ следует производить в следующих случаях:

  • когда требуется исключительная клиентская производительность страницы, например, на главной странице сайта или других страницах с наивысшей посещаемостью;
  • когда суммарная скорость загрузки всех внешних объектов меньше или приблизительно равняется затратам на поочередный запрос каждого из этих объектов по отдельности, т.е. в ситуациях, когда не возникнет уменьшения скорости загрузки страницы из-за невозможности кэширования внешних объектов;
  • когда требуется полная автономность веб-приложения. Встраивать код CSS следует внутрь тега <style>, расположенного в секции <head> страницы, а JavaScript-код — в теги <script> в конце документа, перед закрывающим тегом <body> либо также внутри секции <head>.
  • Следуя приведенным выше рекомендациям, необходимо избегать встраивания CSS- и JS-кода непосредственно в теги веб-страницы (т. е. в атрибуты style, onclick и т. д.). Это исключит дублирование кода на странице, а также упростит его сопровождение.

    Подстановка изображений

    Встраивать изображения прямо в HTML-документ можно, используя схему data:URI. По стандарту RFC 2397 такие URI предназначены для вставки небольших объектов как "непосредственных данных". Синтаксис должен быть следующим:

    data:[<тип данных>][;base64],<данные>

    В случае изображений необходимо указание mime-типа для них (например, image/gif). После него должно располагаться base64-представ-ление бинарного файла с изображением. Ниже приведен пример такой подстановки:

    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQA
    QMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDr
    Az3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkS
    uQmCC" width="16" height="14" alt="Встроенное изображение" />

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

    У данного подхода есть следующие минусы:

  • изображения в base64-представлении имеют размер примерно на 33% больше исходного;
  • необходимо вычислять и обновлять base64-представление изображения каждый раз, когда оно меняется;
  • требуется отдельное решение для браузера Internet Explorer версии 7 и ниже.
  • Решение первой проблемы возможно за счет использования gzip-сжатия файлов CSS, в которых располагаются встроенные изображения. Вторая и третья проблемы легко решаются автоматическим преобразованием изображений в base64-представления при помощи серверных скриптов и созданием альтернативной версии кода для указанных браузеров Internet Explorer.

    Описание способа автоматического встраивания изображений в код по схеме data:URI можно найти в четвертой лекции.

    1.4.4. Исключение избыточных HTTP-запросов

    Уменьшение времени разрешения доменного имени

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

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

    Наилучший способ уменьшить временные издержки, связанные с разрешением доменного имени — использовать наименьшее количество различных хостов для размещения внешних объектов веб-страниц, в особенности для тех объектов, которые требуются для первоначального отображения страницы.

    Идеальным с точки зрения минимизации времени разрешения адреса DNS-сервера считается вариант, когда все объекты расположены на том же хосте, откуда была загружена веб-страница. Чем меньше используется хостов, тем больше вероятность того, что браузер сможет повторно использовать уже установленное соединение.

    На практике же почти у всех браузеров существуют ограничения на количество одновременных соединений с одним хостом. Принимая во внимание это ограничение, наибольший выигрыш в скорости загрузки страниц можно получить, распределив загружаемые объекты по нескольким (4-6) хостам. Подробнее об особенностях параллельной загрузки объектов можно прочитать в пятой главе книги "Разгони свой сайт".

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

    Иногда возникает необходимость перенаправить браузер с одного адреса на другой. Причины чаще всего следующие:

  • добавить косую черту к имени домена;
  • предоставить пользователю документ, перемещенный на другой адрес;I позволить пользователю обращаться к документам сайта даже если он ошибся в написании адреса (например, не набрал www в начале адреса);
  • направить пользователя на другие домены первого уровня, основываясь на его географическом месторасположении и данных об используемом им языке;
  • направить пользователя на определенные страницы в зависимости от того, авторизован он или нет;
  • направить пользователя на страницы с другим протоколом (HTTP или HTTPS);
  • отследить и сохранить действия пользователя и т. д.
  • Какой бы ни была причина, каждый редирект порождает дополнительный HTTP-запрос, занимающий определенное время. Поэтому для страниц, для которых скорость загрузки наиболее критична, число реди-ректов должно быть сведено к минимуму. Для этого необходимо:

  • следить за тем, чтобы ссылки на веб-страницах не вели на адреса, где заведомо будет срабатывать редирект;
  • избегать цепных (последовательных) редиректов;
  • использовать минимальное количество альтернативных адресов для одних и тех же страниц, стараясь предоставить всем пользователям единственный актуальный адрес для каждой страницы;
  • использовать внутренние перенаправления — функцию, доступную в большинстве веб-серверов;
  • использовать средства отслеживания информации о пользователе, не основанные на редиректах;
  • предпочитать серверные редиректы клиентским, которые могут быть заданы при помощи тега <meta> или JavaScript-обработчика. Редиректы, отправляющие браузеру код состояния 300, 301 или 302 и заголовок Location, обрабатываются браузером моментально, а при выполнении клиентских редиректов браузеру требуется дополнительное время на разбор полученной веб-страницы. Кроме того, некоторые браузеры могут кэшировать информацию о ре-директах, тем самым ускоряя повторную загрузку ранее открытых веб-страниц.
  • 1.4.5. Настройка кэширования

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

    Кэширование — это один из наиболее мощных механизмов для уменьшения объема передаваемых по сети данных, притом внедряется этот механизм очень просто. Ниже приведено краткое описание наиболее значимых для кэширования заголовков.

    Заголовок Expires

    Когда HTTP-сервер отправляет объект (например, HTML-документ или изображение) браузеру, он может дополнительно с ответом отправить заголовок Expires с меткой времени. Браузеры обычно хранят ресурс вместе с информацией об истечении его срока действия в локальном кэше. При последующих запросах к тому же объекту браузер сравнивает текущее время и метку времени у находящегося в кэше ресурса. Если метка времени указывает на дату в будущем, браузер загружает ресурс из кэша, не запрашивая его с сервера. Формат должен быть строго следующим:

    день недели(сокр.), число(2 цифры) месяц(сокр.) год часы:минуты:секунды GMT

    Заголовок Expires устанавливает время актуальности информации. Для ресурсов, которые не должны кэшироваться, его нужно выставлять в текущие время и дату (документ устаревает сразу же после получения), для форсирования кэширования его можно определять на достаточно далекую дату в будущем, например:

    Expires: Mon, 27 Dec 2027 00:00:00 GMT

    Заголовок Cache-Control

    Заголовок Cache-Control определяет набор директив, относящихся непосредственно ко времени и специфике кэширования документа. Для запрета кэширования можно выставить его в следующее значение:

    Cache-Control: no-store, no-cache, must-revalidate

    Если же, наоборот, требуется сохранить ресурс в кэш браузера на продолжительный период времени, например, на год (60 * 60 * 24 * 365 секунд), нужно отправлять следующий заголовок:

    Cache-Control: max-age=31536000

    Заголовки Last-Modified, If-Modified-Since

    Заголовок Last-Modified может отправляться сервером для того, чтобы передать браузеру информацию о дате последнего изменения документа. Дата должна задаваться в том же формате, что и в случае с заголовком Expires:

    Last-Modified: Tue, 4 Aug 1995 04:58:08 GMT

    При наличии такой информации в локальном кэше браузер может в следующем запросе отправить ее в заголовке If-Modified-Since:

    If-Modified-Since: Tue, 29 Oct 1994 19:43:31 GMT

    В случае если дата последнего изменения осталась прежней, сервер ответит кодом состояния 304 Not Modified и данные не будут отправлены повторно. В противном случае сервер передаст новую версию файла.

    Данная схема позволяет экономить время, затрачиваемое на передачу данных, однако при ее использовании браузер все равно будет устанавливать соединение с сервером, чтобы узнать, имеется ли более новая версия.

    Заголовки ETag, If-None-Match

    Заголовок ETag является почти полной аналогией заголовка Last-Modified за тем исключением, что в качестве передаваемого значения может выступать произвольная строка. Заголовок отправляется сервером в следующем формате:

    ETag: "any-type-of-tag-or-hash"

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

    If-None-Match: "any-type-of-tag-or-hash"

    И аналогично, если теги совпадают, сервер отвечает кодом состояния 304 Not Modified и данные не передаются повторно. В противном случае сервер передаст новую версию файла.

    Форсированный сброс кэша

    Если время кэширования при помощи заголовков Expires и Cache-Control установлено на несколько лет вперед и требуется сообщить клиентскомубраузеру, что исходный объект изменился, можно воспользоваться двумя способами.

    Можно обновить GET-строку запроса, например, используя номер версии или дату последнего изменения:

    http://testdomain.com/global.css?v1

    http://testdomain.com/global.css?20080901

    А можно добавить номер версии или дату последнего изменения в само имя файла:

    http://testdomain.com/global.v1.css

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

    В спецификации RFC-2616 HTTP-кэшированию посвящена целая глава. В ней подробно рассматривается, как работают все приведенные выше заголовки. Также о многих тонкостях использования кэширования более подробно рассказано в четвертой лекции.

    1.5. Увеличение скорости отображения веб-страниц

    Даже когда веб-страница и все внешние объекты загружены на компьютер пользователя, браузеру по-прежнему требуется время для того, чтобы разобрать страницу, интерпретировать код HTML и CSS, выполнить код JavaScript. Принимая во внимание особенности работы браузеров на этом этапе, можно достичь существенно более высокой скорости загрузки страницы.

    1.5.1. Оптимизация верстки

    Разбирая полученный HTML-код, браузер строит дерево документа, содержащее все элементы страницы. Затем, отыскав все взаимосвязимежду элементами этого дерева и CSS-селекторами, относящимися к данной странице, он применяет к документу стили.

    Если на веб-странице присутствует большое количество элементов или объем CSS-кода достаточно велик, страница может прорисовываться с ощутимой задержкой. Когда объем кода уменьшить уже невозможно, более высокой скорости загрузки можно достичь за счет эффективной верстки. Основные рекомендации к верстке следующие.

  • Наиболее важное содержимое страницы должно находиться в самом начале HTML-документа. Так пользователи смогут начать взаимодействовать с этим содержимым раньше. I Актуальные размеры изображений и ячеек таблиц, содержащих большое количество данных, должны быть явно заданы при помощи HTML-аттрибутов или CSS-свойств. Это позволит избавиться от лишних перерисовок веб-страницы. Например, когда браузер загрузит изображение и определит его размер, ему не потребуется обновлять макет веб-страницы, для изображения уже будет зарезервировано необходимое пространство. Кроме того, точно заданные размеры изображения избавят браузер от избыточной операции масштабирования изображения на лету. I Следует отказаться от использования CSS-expressions для браузеров Internet ExpLorer. Expressions отрицательно влияют на производительность браузера и в большинстве ситуаций могут быть заменены более производительным JS-кодом, а иногда и вовсе альтернативной версткой. В ситуациях же, когда для требуемой функциональности сайта использования expressions не избежать, следует применять одноразовые expressions. I Следует использовать быстродействующие селекторы идентификаторов и селекторы классов. Поскольку большинство браузеров анализируют селекторы справа налево, с виду простой селектор #header .menu li a будет применяться дольше, чем аналогичный ему селектор #header .menu-item. В первом случае браузеру необходимо будет найти все ссылки на странице, проверить, находятся ли они в контексте элемента списка, элемента с классом menu и, наконец, элемента с идентификатором header. Второй вариант более предпочтителен, поскольку поиск элементов по классу и идентификатору выполнится существенно быстрее.
  • Универсальные, дочерние, соседние селекторы, селекторы атрибутов, псевдоклассов и псевдоэлементов должны применяться только в тех ситуациях, когда это действительно необходимо. Все эти разновидности CSS-селекторов существенно более ресурсоемки, чем селекторы идентификаторов или классов.
  • 1.5.2. Особенности отображения веб-страниц

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

    Вот неполный список действий, вызывающих в большинстве браузеров перерисовку страниц или их частей:

  • изменение пользователем размера окна браузера или шрифта;
  • добавление или удаление CSS-кода (как встроенного, так и во внешнем файле);
  • манипуляции с элементами DOM-дерева;
  • изменение следующих свойств элементов страницы: class, font, display, visible, margin, padding, width, height;
  • активация псевдоклассов, таких, например, как :hover.
  • Учитывая эти особенности, можно свести к минимуму число перерисовок страниц, а для того чтобы скорость перерисовок была наивысшей, необходимо:

  • обеспечить минимальную глубину и минимальный размер DOM-дерева, так как часто изменения свойств какого-либо элемента вынуждают браузер перерисовать не только сам этот элемент, но также родительские и дочерние элементы, а иногда и всю ветвь целиком;
  • оптимизировать CSS-селекторы, обеспечить минимальный объем CSS-кода;
  • создавать сложные элементы, анимировать их и производить другие подобные манипуляции над элементами, располагая их вне потока и используя для этого свойства position: absolute и position: fixed ;
  • изменять классы и стили у элементов на максимальной глубине DOM-дерева, что позволит браузеру перерисовывать лишь часть страницы;
  • избегать использования таблиц в верстке, поскольку действия над их ячейками почти всегда вызывают необходимость перерасчитать и перерисовать всю таблицу целиком.
  • 1.6. Оптимизация структуры веб-страниц

    От структуры HTML-документа во многом зависит скорость загрузки страницы пользователем. Даже при одинаковом суммарном размере страниц и равном количестве внешних объектов две различные страницы могут загружаться за совершенно разное время. Причина в том, что отображение элементов частично загруженной страницы в большинстве браузеров осуществляется только после выполнения следующих шагов:

  • получения HTML-документа;
  • получения всех внешних объектов CSS, вызываемых в HTML-документе;
  • получения всех внешних объектов JavaScript, вызываемых в HTML-документе внутри тега <head> ;
  • получения всех внешних объектов JavaScript в HTML-документе внутри тега <body>, расположенных в потоке выше выводящегося элемента.
  • 1.6.1. Особенности загрузки браузерами внешних объектов

    Особенности загрузки кода CSS

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

    Таким образом, более высокой скорости отображения страницы можно добиться, располагая в самом начале веб-страницы, в разделе <head>, все элементы <link>, содержащие вызовы файлов CSS-стилей, а также все встроенные стили, содержащиеся в тегах <style>.

    Особенности загрузки кода JavaScript

    Из-за того, что код JavaScript может изменять содержимое и макет веб-страницы, браузеры приостанавливают отображение элементов, следующих за JS-кодом, до тех пор, пока код не будет загружен и исполнен. Большинство браузеров при этом приостанавливают даже загрузку внешних объектов,следующих за таким JS-кодом. Однако если на момент начала загрузки JS-файла загрузка внешних объектов уже была начата, они будут загружены параллельно.

    Подробный разбор возможной ситуации

    В приведенном ниже примере на веб-странице загружаются два внешних файла CSS и JS.

    <head>
    <script type="text/javascript" src="script-1.js"></script> 
    <link rel="stylesheet" type="text/css" href="style-1.css" /> 
    <script type="text/javascript" src="script-2.js"></script> 
    <link rel="stylesheet" type="text/css" href="style-2.css" /> 
    </head>

    При таком порядке следования в документе и при пустом кэше большинство браузеров будет вынуждено дождаться загрузки первого файла JavaScript, только потом начать загрузку следующих двух файлов CSS и JS и лишь по завершении загрузки второго файла JS загрузить последний файл стилей.

    Всего лишь изменив порядок следования вызовов внешних объектов так, чтобы впереди были вызовы файлов CSS, можно получить ощутимый выигрыш в скорости загрузки.

    <head>
    <link rel="stylesheet" type="text/css" href="style-1.css" /> 
    <link rel="stylesheet" type="text/css" href="style-2.css" />
    <script type="text/javascript" src="script-1.js"></script> 
    <script type="text/javascript" src="script-2.js"></script> 
    </head>

    В этой ситуации браузер может инициировать параллельную загрузку сразу трех внешних объектов — двух файлов стилей и первого файла JS. По окончанию их загрузки браузеру остается загрузить лишь один файл JavaScript, и итоговое время загрузки будет ощутимо ниже.

    Как видно на приведенной ниже диаграмме, в первой ситуации загрузка каждого файла JavaScript блокирует получение остальных внешних объектов, создавая дополнительные задержки. Во второй же ситуации вместе с загрузкой первого файла JS происходит параллельная загрузка максимально возможного количества внешних объектов.

    (рис 1.12) Диаграмма загрузки внешних объектов при различном порядке их вызовов

    Разумеется, экономия времени в каждом отдельном случае будет зависеть от размеров загружаемых объектов и их количества, однако пренебрегать этой особенностью не стоит.

    Следует помнить, что данная особенность неактуальна в браузерах, поддерживающих параллельную неблокирующую загрузку внешних объектов. Перечень таких браузеров можно получить при помощи инструментов, описанных чуть ранее в этой лекции.

    1.6.2. Стадии загрузки страницы

    Во всех браузерах можно выделить несколько основных стадий загрузки страницы. Рассмотрим эти стадии.

  • Предварительная загрузка — предварительное отображение частично загруженной страницы без части рисунков и, в ряде случаев, без интерактивности, без части JavaScript-сценариев.
  • Полная загрузка страницы — отображение полностью загруженной страницы со всеми изображениями, с полностью функционирующими интерактивными частями страницы, полностью функционирующей логикой JavaScript.
  • Чтобы ускорить наступление предварительной загрузки, следует снизить число вызываемых в заголовке страницы файлов до минимума, применить к ним динамическое сжатие, или, для еще большего быстродействия — статическое сжатие. При использовании статического сжатия серверу не придется тратить дополнительное время на сжатие, он будет сразу готов отдать сжатый файл.

    Загрузку файлов, не требующихся на стадии предварительной загрузки, имеет смысл перенести в стадию пост-загрузки.

    Итогом первой стадии загрузки является доставленный и оформленный HTML-документ, с которым пользователь уже может взаимодействовать. Издержки на доставку всех файлов JavaScript должны быть сведены к минимуму, так как на этом этапе они только помешают, замедлив отображение основного содержимого страницы.

    Оптимизация стадии полной загрузки

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

    Необходимо сконфигурировать веб-сервер так, чтобы при запросе любой другой страницы пришлось запрашивать минимум дополнительных объектов. Должен быть также продуман вопрос форсированного сброса кэша в ситуациях, когда это будет необходимо.

    Также при запросе большого количества изображений браузер неизбежно столкнется с ограничением максимального количества соединений на хост, и для обхода этого ограничения необходимо настроить дополнительные серверы для выдачи статического содержимого. Число дополнительных хостов следует напрямую из числа статических файлов, поэтому надо определиться с ними на этапе автоматизации процесса публикации.

    На этой же стадии можно использовать прием объединения изображений.

    Оптимизация стадии пост-загрузки

    К началу этой стадии у пользователя уже должна быть в распоряжении оформленная HTML-страница, на которой все ссылки и формы должны работать без JavaScript.

    На этой стадии могут загружаться любые объекты, которые необходимые для этой или каких-либо других страниц.

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

    Этот обработчик может производить следующие операции:

  • находить необходимые элементы дерева объектов страницы, требующие динамического вмешательства, подключая все необходимые обработчики, события и т.д.;
  • проверять наличие необходимых JavaScript библиотек и по мере надобности загружать их, проверяя, чтобы один и тот же код не загружался дважды;
  • загружать в фоне внешние объекты, используемые на других страницах.
  • В случае применения технологии data:URI для уменьшения количества вызываемых объектов на этой стадии необходимо загружать файл CSS, содержащий все используемые на странице изображения в формате base-64.

    Об отложенной загрузке ресурсов, не требующихся на момент первого отображения страницы, можно прочитать в пятой главе, а также в седьмой главе книги "Разгони свой сайт".

    Прогрессивный подход

    Во многих современных браузерах, в частности, в последних версиях браузера Safari, активно внедряется прогрессивная логика отображения страницы.

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

    Загрузка всех внешних объектов производится параллельно, и разбор содержимого не останавливается. При этом, если производится обращение к свойству стиля или макета, модуль JavaScript приостанавливает свою работу и ожидает загрузку всех незагруженных файлов CSS. Как только все стили загружены, выполнение сценария продолжается с того места, где оно было приостановлено.

    1.6.3. Распределенное хранение контента (CDN)

    У всех браузеров существует ограничение на количество соединений на один хост, находящееся в интервале от 2 до 8 соединений на хост. Для увеличения скорости загрузки внешних объектов можно применять распределенную систему хранения и доставки контента (CDN), организовав или арендовав систему хостов, находящихся на одном или нескольких физических серверах (географически распределенных, если это требуется). Часть хостов должны принимать и обрабатывать запросы пользователей, создавать и передавать результирующие HTML-документы. Остальные хосты должны использоваться только для передачи клиентом статических ресурсов. Благодаря такой схеме скорость доставки контента пользователям будет максимальной, в то же время нагрузка на хостинг веб-сайта может значительно снизиться.

    Необходимо заметить, что разделять по нескольким хостам имеет смысл только изображения и файлы CSS, так как файлы JavaScript почти во всех браузерах загружаются строго последовательно.

    Создать подобную систему распределенного хранения можно либо при помощи автоматического балансировщика, либо установив альтернативные пути к внешним объектам вручную.

    Большое число дополнительных хостов может увеличить временные затраты браузера на установление соединений, поэтому в наибольшем количестве ситуаций предпочтительно использование не более 5 дополнительных хостов (1 основной хост и 4 для параллельной загрузки кэширу-емых объектов) без учета не контролируемых вами хостов, например рекламных. Это позволяет ускорить загрузку приблизительно на 60% в случае большого количества файлов.

    Подробнее о системах распределенного хранения контента (CDN) рассказано в пятой лекции.

    Вернуться к учебному плану