Comments 42
Еще интересен вариант сочетания http/2 и defer: загрузка скрипта и парсинг HTML — могут происходить одновременно, а запуск произойдет в момент, когда HTML будет готов, но раньше, чем это произошло бы при синхронном формировании запросов скриптов по ходу парсинга.
Когда парсер доходит до тега
Является ли блокирующим следующее объявление?
<script type="text/x-template" id="question"> <div> content </div> </script>
Где лучше размещать подобные блоки, в конце страницы или где угодно?
Нет, это не вы старый, это пост устарел :-) Сейчас как раз склеивание всех скриптов в один файл с его минификацией куда более распространено, чем раньше. requirejs, systemjs, webpack — все они обходят рассмотренную тут проблему, делая ее не такой существенной.
Как сказать. Выделение критических ресурсов для более быстрой отрисовки входит в обиход. К примеру, зачем мне ждать загрузки какой-нибудь карты, если я хочу просто ознакомиться с сайтом (например, сайт какой-нибудь компании) и могу до неё никогда и не дойти? Да и на всякие соц. виджеты может быть наплевать.
Да, но при использовании современных инструментов экосистемы javascript выделение критических ресурсов делается совсем другими методами, имеющими мало общего с атрибутами async и defer.
Хм. Простой пример. Вот есть скрипт гугловый, который подключает карту. Вот есть мой некий common.js
, в котором, скажем, описан какой-нибудь полифилл. Сайт встречает заголовком, текстом, формой, кучей текста, картинками и лишь в конце картой. Так вот как раз-таки для скрипта карты и пригодится defer
. Гугл, кстати, так и рекомендует подключать у себя в примере.
… и получаем скрипт, который полностью работоспособен только если на тут же самую страницу подключен другой скрипт? Ну, и еще пара десятков сторонних скриптов?
От этого и стараются уйти, в том числе перечисленными мною инструментами.
Мы ускоряем отрисовку, а не ждём пока у нас загрузятся все ресурсы, которые, возможно, нам и не понадобятся. Чанки о том же, по-сути. Можно собрать всё в единый файл и пусть грузится или же разбить на части и грузить первыми только важные ресурсы, а остальные оставить на потом. Как минимум пользователь уже начнёт взаимодействовать с сайтом, а не будет ждать со словами «сайт тормозной». А, может, и вовсе уйдёт.
Суть одна — но механизмы-то разные! Вы понимаете, что выделение чанков в вебпаке совсем не похоже на простановку атрибутов async или defer скриптам?
Можно прочитать хоть сотню постов про атрибуты async и defer — но когда понадобится разбить пакет на чанки — придется лезть в документацию. И обратное тоже верно.
Например, если есть большой внешний скрипт и небольшой инлайновый сразу после него, то можно ли сделать внешний скрипт deferred?
Если внешний JavaScript-файл размещается непосредственно перед закрывающим тегом body, то использование async и defer становится менее уместным
Менее уместно, но всё же уместно? Например, если у нас 5 внешних скриптов в конце body
то без defer
, они будут загружаться один за другим (так как парсер соответственно поочередно будет переходить от одного тэга script к другому). А вот с defer
у каждого тэга можно загрузку распараллелить. Я правильно понимаю?
Смотрите какая штука.
Вот таким образом он забрал файлы.
А вот таким выполнил:
При этом в FF картина следующая:
При наличии атрибута defer порядок выполнения не определен. Кроме того, в defer скриптах нельзя выполнять document.write().
Скрипты без атрибута будут выполняться строго один за другим, как написано.
При открытом отладчике (или, особенно, Firebug’е) это на практике соблюдается не всегда.
браузер гарантирует, что относительный порядок скриптов с defer будет сохранён.
If the element has a src attribute, and the element has a defer attribute, and the element has been flagged as «parser-inserted», and the element does not have an async attribute
The element must be added to the end of the list of scripts that will execute when the document has finished parsing associated with the Document of the parser that created the element.
The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's «ready to be parser-executed» flag. The parser will handle executing the script.
Тут сказано, что defer скрипты выполняются после парсинга документа, в по мере их загрузки. Кто загрузится первым, тот и выполнится. Только в IE<=9 порядок выполнения был предсказуем, но это поведение не соответствует стандарту и в следующих версиях его починили.
Не однозначно, сказано же, что скрипт добавляется в конец списка скриптов, который (список) будет исполнен после окончание парсинга документа. Сеть же загружает скрипты и помечает их как готовые к выполнению (а не передает на выполнение). В заблуждение вводит последняя фраза, так как не явно указано, или парсер сразу выполнит скрипт, или выполнит только когда дойдёт до него очередь. Но из параграфа выше всё же следует, что парсер выполняет скрипты поочерёдно, иначе бы спецификация явно не указывала бы, что скрипт нужно добавить в список, тем более вконец — в этом не было бы смысла. Ну и ребята из html5rocks о том же говорят.
Смело оставляйте как есть, без defer/async. Браузеры не парсинг html останавливают, а обработку html. То-есть они вполне знают какие скрипты следуют за актуальным и грузят их параллельно. И даже напротив, defer
в хроме замедляет начало их загрузки, по видимому тратиться время на создание отдельного queue
Без defer скрипты грузятся параллельно. С defer создается отдельная очередь, хотя скрипты тоже грузятся параллельно. Преимуществ defer, если скрипты в конце документа попросту нет.
Например, в V8 (используется в Chromium), сделана попытка разобрать все скрипты, независимо от их атрибутов, на отдельном выделенном потоке для выполнения скрипта. Таким образом, «блокирующая парсер» природа JavaScript-файлов должна быть минимизирована по умолчанию.
Блокировку можно уменьшить за счет устранения препарсинга.
В графике для async ошибка, нет там никакой блокировки парсинга. Читайте Илью Григорика https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp
Конечно же, блокировка есть. Любой скрипт может взаимодействовать с DOM и ожидает, что одновременно с его работой DOM меняться не будет. Поэтому парсер не имеет возможности достраивать DOM в процессе исполнения скрипта.
Другое дело, что эта блокировка нарисована непропорционально долгой — нормальный скрипт не может исполняться столько же времени сколько он скачивался.
PS по приведенной вами ссылке этот вопрос воовсе не затрагивался
Асинхронный JavaScript против отложенного