Опубликован: 16.06.2010 | Доступ: свободный | Студентов: 2352 / 93 | Оценка: 4.39 / 4.00 | Длительность: 17:32:00
ISBN: 978-5-9963-0253-6
Лекция 6:

Оптимизация структуры веб-страниц

< Лекция 5 || Лекция 6: 123456 || Лекция 7 >

5.5. Стыкуем компоненты в JavaScript

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

Ан нет. Не зря упоминается в самом начале прошлого раздела о событии onload / onreadystatechange для скриптов. Используя их, мы можем однозначно привязать некоторый код к окончанию загрузки конкретного модуля. Дело за малым: нам нужно определить этот самый код каким-либо образом.

5.5.1. Решение первое: дерево загрузки


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

var modules = [
[0,  'iteml', function(){
alert('item1 is loaded'); }],
[1,  'item2', function(){
alert('item2 is loaded'); }],
[1,  'item3', function(){ alert('item3 is loaded');
];
Листинг 5.17.

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

Давайте рассмотрим, каким образом можно использовать данную структуру.

/* перебор и загрузка модулей */ function load_by_parent (i) { i = i || 0;
var len = modules.length, module;
/* перебираем дерево модулей */ while (len—) {
module = modules[len]; /* и загружаем требуемые элементы */ if (!module[0]) { loader(len);
}
}
}
/* объявляем функцию-загрузчик */ function loader (i) {
var module = modules[i]; /* создаем новый элемент script */
var script = document.createElement('script'); script.type = 'text/javascript'; /* задаем имя файла */
script.src = module[1] + '.js'; /* задаем текст внутри тега для запуска по загрузке */
script.text = module[2]; /* запоминаем текущий индекс модуля */
script.title = i + 1; /* выставляем обработчик загрузки для IE *
/ script.onreadystatechange = function() { if (this.readyState === 'loaded') 
{ /* перебираем модули и ищем те, которые нужно загрузить */ load_by_parent(this.title);
}
};
/* выставляем обработчик загрузки для остальных */
script.onload = function (e) { /* исполняем текст внутри тега (нужно только для Opera) 
*/ if (/opera/i.test(navigator.userAgent)) { eval(e.target.innerHTML);
}
/* перебираем модули и ищем те, которые нужно загрузить */ load_by_parent(this.title);
};
/* прикрепляем тег к документу */
document.getElementsByTagName('head')[0].appendChild(script);
}
/* загружаем корневые элементы */ load_by_parent();
Листинг 5.18.

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

5.5.2. Решение второе: загрузка через DOM-дерево

Кроме того, подобной проблемой уже занимался Андрей Сумин и даже предложил свое решение в виде библиотеки JSX (http://jsx.ru/), которая позволяет назначать список зависимостей через DOM-дерево. Для этого у элементов, которые требуют загрузки каких-либо модулей для взаимодействия с пользователем, назначается класс с префиксом jsx-compo-nent, а далее идет уже список компонентов. Сама библиотека обходит DOM-дерево, находит все модули, которые нужно загрузить, и последовательно их загружает. Просто замечательно.

Но что, если нам требуется поменять обработчик по загрузке этого модуля? Как его задавать? Сама JSX использует атрибуты искомых узлов DOM-дерева сугубо для определения параметров этих модулей. Это достаточно удобно: ведь таким образом можно назначить и инициализатор модуля.

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

5.5.3. Решение третье: JSX+7YASS

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

Для примера можно рассмотреть следующий участок HTML-кода:

<div id="item1" class="yass-module-utils-base-dom">
<span id="item2" class="yass-module-dom"
title="_('#item2')[0].innerHTML = 'component is loading...';"> </span> </div>
Листинг 5.19.

Давайте разберемся, какую логику загрузки он обеспечивает.

  1. YASS при инициализации обходит DOM-дерево документа и выбирает все узлы с классом yass-module-*.
  2. После этого формируется два потока загрузки модулей: для dom-base-utils и для dom.Причем в последнем случае загрузки фактически не будет: загрузчик дождется, пока состояние компонента dom будет выставлено в loaded, и только потом запустит (черeз eval ) код, записанный в title этого элемента (в данном случае это span ).
  3. Первый поток загрузки асинхронно вызовет три файла с сервера: yass.dom.js, yass.base.js и yass.utils.js. По загрузке всех этих модулей (ибо они вызваны в цепочке зависимостей, и в данном случае dom зависит от base, который зависит от utils ) будут вызваны соответствующие инициализационные функции (если они определены). Таким образом, возможны два типа обработчиков: непосредственно по загрузке компонента (будет вызвано для всех компонентов в цепочке) и после загрузки всей заданной цепочки компонентов (в нашем случае это dom-base-utils ).
  4. Если мы хотим каким-то образом расширить нашу цепочку, то можем в конце каждого из указанных файлов прописать загрузку какой-либо другой цепочки (например, base-callbacks ), которая "заморозит" загрузку модуля base до получения callbacks. Сделать это можно (имея в виду, что расширяем зависимости модуля base) следующим образом:
    _.load('callbacks-base')
    ;
  5. Предыдущий шаг может быть также выполнен при помощи самого DOM-дерева: нам нужно будет прописать для произвольного элемента класс yass-module-callbacks-base. Это добавит в дерево зависимостей искомую цепочку.

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

dom-> base -> utils -> callbacks

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

Естественно, весь указанный функционал уже добавлен в последнюю версию YASS (http://yass.webo.in/).

5.6. Что такое CDN и с чем его едят

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

CDN (англ. Content Delivery Network) — распределенная сеть хранения данных. Предназначена как для обеспечения отказоустойчивости, так и для максимально быстрого времени ответа при запросе файлов. Информация данного раздела подготовлена при помощи специалистов первой CDN в России — NGENIX.

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

Схема CDN, источник: ngenix.net

Рис. 5.12. Схема CDN, источник: ngenix.net

5.6.1. Доступность контента

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


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

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

5.6.2. Высокая скорость загрузки

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

В качестве примера стоит привести следующие цифры: время ответа обычного сервера, расположенного на VPS (даже не на shared-хостинге), составляет 50-200 мс (в зависимости от различных условий, в том числе от загруженности канала). Для CDN это число очень редко превышает 10 мс.

5.6.3. Снижение нагрузки на сервер — источник информации

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

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

5.6.4. Размещение "тяжелого" контента

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


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

5.6.5. Отказоустойчивость и безопасность

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

5.6.6. Масштабируемость и эластичность по нагрузке

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

5.6.7. CDN в России

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

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

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

< Лекция 5 || Лекция 6: 123456 || Лекция 7 >
Ольга Артёмова
Ольга Артёмова

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

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

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

Наталья Алмаева
Наталья Алмаева
Россия
Вадим Барков
Вадим Барков
Украина, Киев