завантажувати та виконувати замовлення сценаріїв


265

Існує так багато різних способів включити JavaScript на сторінку html. Я знаю про наступні варіанти:

  • вбудований код або завантажений із зовнішнього URI
  • включено в тег <head> або <body> [ 1 , 2 ]
  • відсутність deferабо asyncатрибут (лише зовнішні сценарії)
  • включається в статичне джерело або додається динамічно іншими сценаріями (у різних станах розбору, різними методами)

Не рахуючи браузерних onEventскриптів із жорсткого диска, javascript: URI та -attributes [ 3 ], вже існує 16 альтернатив для виконання JS, і я впевнений, що щось забув.

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

Оскільки я побоююся, що цього немає, ось моє конкретне питання: у мене є кілька (зовнішніх) головних сценаріїв для ініціалізації та завантаження сценарію. Тоді у мене в кінці тіла два статичні, вбудовані сценарії. Перший дозволяє завантажувачу сценарію динамічно додавати інший елемент сценарію (посилаючись на зовнішній js) до тіла. Другий із статичних, вбудованих сценаріїв хоче використовувати js із доданого зовнішнього скрипту. Чи можна покластися на того, хто був страчений (і чому :-)?


Ви подивилися на завантаження сценаріїв без блокування Стівом Суудерсом? Це трохи датовано зараз, але все ж містить корисну інформацію про поведінку браузера з урахуванням конкретної методики завантаження сценарію.
Джош Хабдас

Відповіді:


331

Якщо ви не завантажуєте динамічно сценарії або не позначаєте їх як deferабо async, то сценарії завантажуються у порядку, який зустрічається на сторінці. Не має значення, чи це зовнішній сценарій чи вбудований сценарій - вони виконуються в тому порядку, з яким вони стикаються на сторінці. Вбудовані сценарії, що надходять після зовнішніх скриптів, зберігаються до тих пір, поки всі зовнішні скрипти, що надійшли до них, не завантажуються та не запускаються.

Сценарії асинхронізації (незалежно від того, як вони вказані як асинхронізація) завантажуються та виконуються у непередбачуваному порядку. Браузер завантажує їх паралельно, і він може запускати їх у будь-якому порядку.

Немає передбачуваного порядку серед кількох речей асинхронізації. Якщо потрібен передбачуваний порядок, його потрібно буде закодувати, зареєструвавши повідомлення про завантаження із сценаріїв асинхронізації та послідовно виконуючи вручну виклики javascript, коли завантажуються відповідні речі.

Коли тег скрипту буде вставлено динамічно, то, як поводиться порядок виконання, залежатиме від браузера. У цій довідковій статті ви можете подивитися, як веде себе Firefox . Коротше кажучи, новіші версії Firefox за замовчуванням динамічно додаються теги сценарію для асинхронізації, якщо тег сценарію не встановлено інакше.

Тег сценарію з asyncможе запускатися, як тільки він завантажується. Насправді, браузер може призупинити аналізатор від того, що він робив, і запустити цей сценарій. Отже, це дійсно може працювати майже в будь-який час. Якщо сценарій був кешований, він може працювати майже відразу. Якщо сценарій завантажується деякий час, він може запуститися після того, як буде виконано аналізатор. Єдине, що слід пам’ятати, asyncце те, що він може працювати в будь-який час і цей час не передбачуваний.

Тег сценарію deferчекає, поки буде виконаний весь аналізатор, а потім запускає всі скрипти, позначені deferв тому порядку, в якому вони зустрічалися. Це дозволяє позначити кілька сценаріїв, які залежать один від одного як defer. Усі вони будуть відкладені, поки не буде виконано аналіз документа, але вони будуть виконуватись у порядку, з яким вони стикалися, зберігаючи свої залежності. Я думаю про deferте, що сценарії випадають у чергу, яка буде оброблена після того, як буде зроблено аналізатор. Технічно браузер може завантажувати сценарії у фоновому режимі в будь-який час, але вони не виконуватимуть чи блокуватимуть аналізатор, поки після того, як буде виконано аналіз, розбирається сторінка та аналізується і виконується будь-які вбудовані сценарії, які не позначені deferабо async.

Ось цитата з цієї статті:

Сценарії, вставлені в скрипт, виконують асинхронно в IE та WebKit, але синхронно в Opera та Firefox до 4.0.

Відповідна частина HTML5 специфікації (для нових сумісних браузерів) знаходиться тут . Там багато написано про поведінку асинхронізації. Очевидно, ця специфікація не застосовується до старих браузерів (або невідповідних веб-переглядачів), поведінку яких, ймовірно, доведеться перевірити, щоб визначити.

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

Тоді необхідно дотримуватися першого з наступних варіантів, який описує ситуацію:

Якщо в елементі є атрибут src, а в елементі є атрибут defer, а елемент позначений як "вставлений аналізатор", а елемент не має атрибута асинхронізації . Елемент повинен бути доданий до кінця списку сценарії, які будуть виконані після завершення розбору документа, пов'язаного з документом аналізатора, який створив елемент.

Завдання, яке джерело завдань для мережі розміщує у черзі завдань після завершення алгоритму вилучення, повинно встановлювати прапор елемента "готовий до парсера". Аналізатор буде обробляти виконання сценарію.

Якщо в елементі є атрибут src, а елемент позначений як "вставлений аналізатор", а елемент не має атрибута асинхронізації . Елемент є сценарієм блокування розбору, що очікує на розгляд, в документі аналізатора, який створив елемент. (Одночасно може існувати лише один такий сценарій на Документ.)

Завдання, яке джерело завдань для мережі розміщує у черзі завдань після завершення алгоритму вилучення, повинно встановлювати прапор елемента "готовий до парсера". Аналізатор буде обробляти виконання сценарію.

Якщо в елементі немає атрибуту src, а елемент позначено як "вставлений парсер", а Документ парсерів HTML або XML-аналізатор, який створив елемент сценарію, має таблицю стилів, що блокує сценарії . очікує сценарій блокування синтаксичного розбору документу аналізатора, який створив елемент. (Одночасно може існувати лише один такий сценарій на Документ.)

Встановіть прапор елемента "готовий до парсера". Аналізатор буде обробляти виконання сценарію.

Якщо в елементі є атрибут src, у нього немає атрибута асинхронізації та не встановлено прапор "force-async", елемент повинен бути доданий до кінця списку сценаріїв, які виконуватимуться для того, щоб якнайшвидше пов'язати з елементом «Документ сценарію» в момент запуску алгоритму підготовки сценарію.

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

Якщо елемент зараз не є першим елементом у списку скриптів, які виконуватимуться для того, щоб якнайшвидше до нього було додано вище, то позначте елемент як готовий, але скасуйте ці кроки, ще не виконуючи сценарій.

Виконання: виконайте блок скриптів, відповідний першому елементу скрипту в цьому списку сценаріїв, який буде виконуватися для того, щоб якнайшвидше.

Видаліть перший елемент із цього списку скриптів, які виконуватимуться якнайшвидше.

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

Якщо в елементі є атрибут src, елемент повинен бути доданий до набору сценаріїв, який буде виконаний якнайшвидше Документу елемента сценарію в момент запуску алгоритму підготовки сценарію.

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

В іншому випадку Агент користувача повинен негайно виконати блок скриптів, навіть якщо інші сценарії вже виконуються.


Що з скриптами модуля Javascript type="module",?

Тепер у Javascript передбачена підтримка завантаження модуля з таким синтаксисом:

<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>

Або з srcатрибутом:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

Усім сценаріям type="module"автоматично надається deferатрибут. Це завантажує їх паралельно (якщо вони не вбудовані в рядки) з іншим завантаженням сторінки, а потім запускає їх у порядку, але після того, як буде виконано аналіз.

Сценаріям модулів також може бути наданий asyncатрибут, який запустить сценарії вбудованого модуля якомога швидше, не чекаючи, поки виконаний аналізатор, і не чекаючи запуску asyncсценарію в будь-якому конкретному порядку відносно інших сценаріїв.

У цій статті є досить корисна діаграма тимчасової шкали, яка показує отримання та виконання різних комбінацій скриптів, включаючи скрипти модулів, у цій статті: Завантаження модуля Javascript .


Дякую за відповідь, але проблема в тому , сценарій буде динамічно додаються на сторінку, яка означає , що вважається асинхронними . Або це працює лише в <head>? І мій досвід також полягає в тому, що вони виконуються в порядку документа?
Бергі

@Bergi - Якщо він додається динамічно, він асинхронізується, а порядок виконання не визначений, якщо ви не пишете код для управління ним.
jfriend00

Просто, Колінк констатує навпаки ...
Бергі

@Bergi - Гаразд, я змінив свою відповідь, щоб сказати, що сценарії асинхронізації завантажуються у невизначеному порядку. Їх можна завантажувати в будь-якому порядку. Якби я був ти, я б не розраховував, що спостереження Колінка є таким, яким воно є завжди. Я не знаю жодного стандарту, який би стверджував, що динамічно доданий скрипт повинен бути запущений негайно і повинен блокувати інші сценарії, поки він не завантажується. Я б очікував, що це буде залежати від браузера, а також, можливо, залежати від факторів навколишнього середовища (кешований сценарій тощо).
jfriend00

1
@RuudLenders - Це залежить від реалізації браузера. Ознайомлення з тегом скрипту раніше в документі, але позначене символом deferдає аналізатору можливість розпочати його завантаження швидше, поки відкладає його виконання. Зауважте, що якщо у вас багато сценаріїв з одного хоста, то швидше запуск завантаження може фактично уповільнити завантаження інших з того самого хоста (оскільки вони змагаються за пропускну здатність), на яку ваша сторінка чекає (це не deferтак) це може бути меч з двома кінцями.
jfriend00

13

Браузер виконує сценарії в тому порядку, в якому їх знаходить. Якщо ви викликаєте зовнішній скрипт, він заблокує сторінку, поки сценарій не буде завантажений та виконаний.

Щоб перевірити цей факт:

// 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>тегу та запустите новий.


Я можу підтвердити таку поведінку. Але на наших сторінках зворотного зв’язку є підказки, які можуть працювати лише тоді, коли тестується кеш-файл test.php. Чи знаєте ви якісь специфікації та довідкові посилання з цього приводу?
Бергі

4
link.js не блокує. Використовуйте сценарій, подібний до вашого PHP, щоб імітувати тривалий час завантаження.
1983 р.

14
Ця відповідь невірна. Не завжди буває так, що "динамічно додані сценарії виконуються, як тільки вони додаються до документа". Іноді це так (наприклад, для старих версій Firefox), але зазвичай це не так. Порядок виконання, як згадується у відповіді jfriend00, не є визначеним.
Фабіо Белтраміні

1
Немає сенсу, що сценарії виконуються в тому порядку, який вони відображаються на сторінці, незалежно від того, вбудовані вони чи ні. Чому тоді фрагмент менеджера тегів Google та багато інших, яких я бачив, мали код вставити новий скрипт над усіма іншими тегами скриптів на сторінку? Цього не було б сенсу, якби вищезазначені сценарії вже були завантажені напевно ?? чи я щось пропускаю.
користувач3094826


2

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

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'])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.