Статьи

Ленивая загрузка асинхронного Javascript

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

Проблема в том, как загрузить внешний javascript с минимальным влиянием на сайты ваших клиентов? Вот мои требования:

  1. Малый . Я не хочу, чтобы на их сайтах был большой беспорядок. 10-15 строк, топы.
  2. Автономные . Среда неизвестна, поэтому мы не можем полагаться на какие-либо внешние зависимости, такие как библиотеки javascript.
  3. Кроссбраузерный . Я понятия не имею, какие браузеры у меня есть у клиентов, поэтому я не могу сделать что-то современное или модное, не совместимое с предыдущими версиями. Я предполагаю, по крайней мере, IE6 и выше, хотя.
  4. Асинхронная загрузка . Загрузка моего скрипта не должна блокировать загрузку любого скрипта на их сайтах.
  5. Ленивая загрузка . Если мой сайт временно работает медленно, я не хочу блокировать запуск события onload до тех пор, пока наш сайт не ответит.
  6. Сохранить события . Любые используемые события не должны переопределять какие-либо события на сайте клиента. Минимальное воздействие, как я уже сказал.
  7. Не загрязняйте пространство имен . Глобальных переменных следует избегать, поскольку они могут конфликтовать с существующим JavaScript.

Примечание: я не сделал все это сам. Многие люди сделали, я просто записываю это для вас. Спасибо: Джонатан , Стивен , Питер и Линус .

Сценарий тег

<script type="http://yourdomain.com/script.js"></script>

Будучи автономным, кросс-браузерным и самым коротким фрагментом кода; он не загружается асинхронно и не загружается.
Провал .

Снимок экрана из сетевой консоли Firebug: сценарий (настроенный на загрузку в течение 2 секунд) блокирует загрузку большого изображения (добавленного после тега сценария, приведенного выше, и используемого в этой статье в качестве теста). Событие загрузки (красная линия) срабатывает через 2,46 секунды.

Асинхронный шаблон (тег сценария, написанный на JavaScript)

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

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

(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();

Примечание : async — это атрибут HTML5 , делающий именно то, что мы пытаемся смоделировать с помощью нашего хака, поэтому он добавлен для хорошей меры. Кроме того, перенос кода в анонимную функцию предотвращает утечку любых переменных в остальную часть документа.

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

Снимок экрана из сетевой консоли Firebug: сценарий (настроенный на загрузку за 2 секунды) загружается параллельно с большим изображением. Событие Onload (красная линия) срабатывает через 2,02 секунды.

Схема отложенной загрузки (асинхронная схема запускается при загрузке)

Итак, как сделать так, чтобы не блокировать нагрузку? Ну, вы заключаете свой код в функцию, которая вызывается при загрузке. Когда срабатывает событие onload, вы знаете, что не заблокировали его.

window.onload = function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}

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

Ненавязчивая ленивая нагрузка

Логическим решением вышеупомянутой проблемы является использование воплощения addEvent. addEvent — это просто общее имя для кросс-браузерного способа взять текущую функцию, привязанную к onload, добавить ее в очередь, добавить свою функцию в очередь и привязать очередь к событию onload. Так какую версию addEvent нам следует использовать?

Были конкурсы на написание короткой и компактной версии addEvent , и победителем этого конкурса стал Джон Резиг с этой маленькой красавицей:

function addEvent(obj, type, fn)  {
if (obj.attachEvent) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn](window.event);}
obj.attachEvent('on'+type, obj[type+fn]);
} else
obj.addEventListener(type, fn, false);
}

Примечание . Это небезопасный код, поскольку он основан на сериализации функции в строку, что отключено в мобильных браузерах Opera .

Дело в том, что нам не нужны все эти общие события, мы имеем дело только с нагрузкой здесь. Поэтому, если мы сначала заменим атрибут type на жестко закодированный «load», заменим obj на «window» и удалим исправление, заставляющее «this» работать в IE, у нас останется четыре строки кода. Давайте совместим это с приведенным выше ленивым шаблоном загрузки:

(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();

Это именно то, что мы ищем здесь.
В заключение!

Снимок экрана из сетевой консоли Firebug: скрипт (настроенный на загрузку через 2 секунды) загружается после того, как сработало событие onload. Событие загрузки (красная линия) срабатывает через 0,41 секунды.

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