загрузить и выполнить порядок скриптов - PullRequest
233 голосов
/ 25 января 2012

Существует множество способов включить JavaScript в html-страницу. Я знаю о следующих опциях:

  • встроенный код или загруженный с внешнего URI
  • включено в тег или [ 1 , 2 ]
  • без атрибута defer или async (только внешние скрипты)
  • включен в статический источник или динамически добавлен другими скриптами (при разных состояниях разбора, разными методами)

Не считая скрипты браузера с жесткого диска, javascript: URIs и onEvent -atatributes [ 3 ], уже есть 16 вариантов выполнения JS, и я уверен, что что-то забыл.

Меня не очень беспокоит быстрая (параллельная) загрузка, мне более интересно узнать порядок выполнения (который может зависеть от порядка загрузки и порядка документов ). Есть ли хорошая (кросс-браузерная) ссылка, которая охватывает действительно все случаи? Например. http://www.websiteoptimization.com/speed/tweak/defer/ имеет дело только с 6 из них и тестирует в основном старые браузеры.

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

Ответы [ 4 ]

290 голосов
/ 25 января 2012

Если вы не загружаете скрипты динамически или не помечаете их как defer или async, тогда скрипты загружаются в порядке, встречающемся на странице. Неважно, является ли это внешним скриптом или встроенным скриптом - они выполняются в порядке, в котором они встречаются на странице. Встроенные сценарии, которые приходят после внешних сценариев, сохраняются до тех пор, пока все внешние сценарии, которые были до них, не загружены и запущены.

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

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

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

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

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

Вот цитата из этой статьи:

скрипты, вставленные скриптами, выполняются асинхронно в IE и WebKit, но синхронно в опере и до 4.0 Firefox.

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

Цитата из спецификации HTML5:

Тогда первый из следующих вариантов, описывающих ситуацию необходимо соблюдать:

Если элемент имеет атрибут src и элемент имеет отложенный атрибут, и элемент был помечен как «вставленный парсер», и элемент не имеет асинхронного атрибута Элемент должен быть добавлен до конца списка скриптов, которые будут выполняться, когда документ завершил анализ, связанный с документом синтаксического анализатора, который создал элемент.

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

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

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

Если элемент не имеет атрибута src, и элемент был помечен как «вставленный синтаксический анализатор», а также документ HTML-анализатор или синтаксический анализатор XMLу созданного элемента script есть таблица стилей, которая блокирует сценарии Элемент является ожидающим сценарием блокировки синтаксического анализа в документе синтаксического анализатора, создавшего элемент.(Одновременно может быть только один такой скрипт для каждого документа.)

Установите флаг элемента "готов к выполнению парсером".Парсер будет обрабатывать выполнение скрипта.

Если элемент имеет атрибут src, не имеет атрибута async и не имеет установленного флага force-async Элемент должендобавьте в конец списка сценариев, которые будут выполняться по порядку при первой возможности, связанной с элементом Document элемента script во время запуска алгоритма подготовки сценария.

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

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

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

Удалить первый элемент изm этот список сценариев, которые будут выполняться по порядку как можно скорее.

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

Если элемент имеет атрибут src Элемент должен быть добавлен к набору сценариев, которые будут выполняться как можно быстрее документа Document ofЭлемент сценария во время запуска алгоритма подготовки сценария.

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

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

11 голосов
/ 25 января 2012

Браузер выполнит сценарии в порядке их поиска. Если вы вызываете внешний скрипт, он блокирует страницу, пока скрипт не будет загружен и выполнен.

Чтобы проверить этот факт:

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>

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

Чтобы проверить этот факт:

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = "link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
    </script>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>

Порядок оповещений «добавлен» -> «Привет!» -> "финал"

Если в скрипте вы пытаетесь получить доступ к элементу, который еще не был достигнут (пример: <script>do something with #blah</script><div id="blah"></div>), то вы получите ошибку.

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

5 голосов
/ 21 февраля 2019

Отличное резюме от @ addyosmani

enter image description here

Бесстыдно скопировано с https://addyosmani.com/blog/script-priorities/

1 голос
/ 18 июня 2019

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

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...