Download - non-blocking java script
Non-Blocking JavaScriptDmitriy Yakubovskiy
Параллельные загрузки
Потоки по браузерам *
Firefox 3+ 6 потоков
Chrome 6 потоков
Safari 3-4 4 потока
Safari 56 потоков
Opera 12 6 потоков
IE 6-7 2 потока
IE 8-96 потоков
IE 108 потоков
* - число параллельных соединений к одному хосту
Процесс отрисовки
Исходный HTML DOM дерево Дерево отрисовки
DOMContentLoaded
- событие срабатывает при загрузке документа, кроме IE<9
- ждет css, если после него идет скрипт
- ждёт загрузки и выполнения скриптов (кроме скриптов с
async/defer, если есть поддержка и динамических)
- в FF/Chrome формы автозаполняются по
DOMContentLoaded
- интерфейсы обычно инициализируются по
DOMContentLoaded
DOMContentLoaded (Firefox)
Скрипты на странице*
Во время загрузки
Во время выполнения
- блокируется отрисовка страницы- блокируют вообще все загрузки (IE<8, Opera<15)- блокируют все загрузки, кроме стилей и скриптов (IE8, FF, Chrome, Safari)- остальные загрузки не блокируются (IE>8)
- ждет пока загрузятся предшествующие css файлы- блокируют вообще всё
* - распространяется на скрипты со всех хостов
Скрипты в HEAD
IE6-7
Скрипты в HEAD
IE8
Скрипты в HEAD
IE9
Скрипты в HEAD
Сhrome / Safari / FF
Скрипты в конце BODY
Chrome
- не блокируется рендеринг страницы- не блокируются загрузки предыдущих ресурсов- страница доступна до инициализации скриптов
Заглушки интерфейсов
<link rel="stylesheet" type="text/css" href="styles.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" type="text/css" href="ie.css">
<![endif]-->
Conditional Comments (IE)
Conditional Comments (IE)
<!--[if IE]><![endif]-->
<link rel="stylesheet" type="text/css" href="styles.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" type="text/css" href="ie.css">
<![endif]-->
Не блокирующие скрипты
- document.write- script in iframe- script defer- script async- XMLHttpRequest Eval- XMLHttpRequest Injection- Script DOM
document.write
IE6
document.write('<scr'+'ipt src="jquery.js"></scr'+'ipt>');
document.write('<scr'+'ipt src="scripts.js"></scr'+'ipt>');
- не блокирует другие скрипты- сохраняет порядок выполнения- блокирует рендеринг, загрузку css/img- актуально только для IE<8, Opera<15
script in iframe
- не блокирует рендеринг / загрузки- необходимо вносить изменения в скрипты- ограничение по домену
<iframe src='script.html' id="f1"></iframe>
<iframe src='script2.html' id="f2"></iframe>
script defer- по стандарту только для внешних скриптов- не блокирует рендеринг / загрузки- выполнение после рендеринга страницы- сохраняется порядок выполнения (кроме IE<10)- IE4+, FF3.1+, Chrome 8+, Safari 5.1+, Opera 15+<script src="jquery.js" defer></script>
<script src="scripts.js" defer></script>
script async- по стандарту только для внешних скриптов- не блокирует рендеринг / загрузки- выполнение сразу после загрузки- не сохраняется порядок выполнения- IE10+, FF3.6+, Chrome 8+, Safari 5.1+, Opera 15+<script src="script1.js" async></script>
<script src="script2.js" async></script>
XMLHttpRequest Eval
var xhr = getXmlHttp();
xhr.open("GET", "script.js", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
eval.call(window, xhr.responseText);
}
}
xhr.send(null);
- не блокирует рендеринг / загрузки- не сохраняется порядок выполнения- ограничение по домену- нет индикации загрузки
XMLHttpRequest Injection- не блокирует рендеринг / загрузки- не сохраняется порядок выполнения- ограничение по домену- нет индикации загрузкиvar xhr = getXmlHttp();xhr.open("GET", "script.js", true);xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { var s=document.createElement('script'); document.getElementsByTagName('head')[0] .appendChild(s); s.text = xhr.responseText; }} xhr.send(null);
Script DOM
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'app.js';
document.getElementsByTagName('head')[0]
.appendChild(script);
- не блокирует рендеринг / загрузки- не сохраняется порядок выполнения, кроме Opera<15- async:true для Firefox 3.6, чтобы не сохранять порядок- нет ограничений на домен
Script DOM + async:false
- не блокирует рендеринг / загрузки- сохраняется порядок выполнения (кроме браузеров, которые не поддерживают async, но не FF<3.6 и Opera)- нет ограничений на домен
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = false;
script.src = 'app.js';
document.getElementsByTagName('head')[0]
.appendChild(script);
Script DOM + onreadystatechange
- не блокирует рендеринг / загрузки- позволяет гибко управлять загрузкой / выполнением- нет ограничений на домен- только IE6+
var js = document.createElement('script');
js.onreadystatechange = function() {
if (js.readyState == 'loaded') {
document.body.appendChild(js); // execution
}
};
js.src = 'app.js';
Порядок загрузки не важен
Цель:
- Загрузить скрипты параллельно- Выполнить сразу после загрузки- Не блокировать рендеринг- Не блокировать загрузку других ресурсов- Кроссбраузерное решение
Решение: Script DOM
Порядок загрузки важен
Цель:
- Загрузить скрипты параллельно- Выполнить последовательно- Не блокировать рендеринг- Не блокировать загрузку других ресурсов- Кроссбраузерное решение
Решение: Script DOM + async:false / onreadystatechange для IE / document.write для остальных браузеров
Техники связывания
- window onload- timer- script onload
- callback (m)- degrading script tags (m)
m - требует модификации скриптов
Техники связывания
<script type="text/javascript">
function init() {
...
App.start();
...
}
init();
</script>
<script type="text/javascript" src="app.js"></script>
window onload
if ( window.addEventListener ) {
window.addEventListener("load", init, false);
}
else if ( window.attachEvent ) {
window.attachEvent("onload", init);
}
- просто в реализации- инициализация срабатывает слишком поздно
timer
function timer(interval) { if ( typeof(jQuery) === "undefined" ) { setTimeout(timer, interval); } else { init(); }}timer(300);
- просто в реализации- лишние расходы ресурсов на выполнение скрипта- задержка при слишком большом интервале
script onload
js.onload = function() { if ( !js.done ) { js.done = true; init(); }};js.onreadystatechange = function() { if ( !js.done && js.readyState.match(/loaded|complete/) ) { js.done = true; init(); }};
- выполнение инлайн скрипта максимально быстро- немного сложнее в реализации
callback (m)
- выполнение инлайн скрипта максимально быстро- вызов init() в конце app.js- не гибкое решение- требует модификации скриптов
function init() { ... }
var js = document.createElement('script');
js.src = "app.js"; document.getElementsByTagName('head')
[0].appendChild(js);
degrading script tags (m)
function init() { ... }var js = document.createElement('script');js.src = "app.js";js.text = "init();"; document.getElementsByTagName('head')[0].appendChild(js);
- гибкое решение- требует модификации скриптов
inline script
<script type="text/javascript" src="app.js"> init();</script>
degrading script tags (m)
var fs = document.getElementsByTagName("script");
var len = fs.length;
while ( len ) {
var s = fs[len-1];
if ( s.src.indexOf('app.js') != -1 ) {
eval( s.innerHTML );
break;
}
len--;
}
app.js
Загрузчики
- $script.js- YepNope.js- Control.js- LAB.js- Head.js- Load.js- Require.js
Как будем загружать сам загрузчик?
Загрузка виджетов
● Добавление через DOM
● Связывание через onload/onreadystatechange
● document.write()○ Делаем свой document.write()○ Выносим виджет в отдельный
невидимый элемент○ Загружаем в скрытый iframe,
по onload копируем на страницу
Загрузка виджетов(function(d, s) {
var js, fjs = d.getElementsByTagName(s)[0],
load = function(url, id) {
if (d.getElementById(id)) {return;}
js = d.createElement(s);
js.src = url;
js.id = id;
fjs.parentNode.insertBefore(js, fjs);
};
load('widget1.js', 'fbjssdk');
load('widget2.js', 'gplus1js');
load('widget3.js', 'tweetjs');
}(document, 'script'));
Преимущества подхода
Страница не виснет из-за сторонних виджетов
Управление виджетами из одного места
Гарантия подключения узла всего один раз (ID)
Постзагрузка виджетов
Постзагрузка виджетов
Полезные материалы
Ресурсыwww.browserscope.orgwww.stevesouders.com
КнигаSteve Souders - Even Faster Web Sites
Таблица сравнения загрузчиковhttps://spreadsheets.google.com/a/sysiq.com/lv?key=tDdcrv9wNQRCNCRCflWxhYQ
● WebPageTest Online (www.webpagetest.org)
● Webkit Developer Toolbar (Timeline Tab)
● Firebug for FF (YSlow add-on, Net Tab)
● Fiddler (Web Debugging Proxy)
● DynaTrace Ajax Edition
● Sloppy (proxy server)
Инструменты
Вопросы?
Задавайте умные вопросы.
Получайте умные ответы.