Как именно работает <script defer = "defer">? - PullRequest
200 голосов
/ 09 марта 2011

У меня есть несколько <script> элементов, и код в некоторых из них зависит от кода в других <script> элементах. Я видел, что атрибут defer может пригодиться здесь, поскольку он позволяет откладывать блоки кода при исполнении.

Чтобы проверить это, я выполнил это в Chrome: http://jsfiddle.net/xXZMN/.

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

Однако, это предупреждает 2 - 1 - 3. Почему это не настораживает 1 - 2 - 3?

Ответы [ 10 ]

162 голосов
/ 09 марта 2011

Несколько фрагментов из спецификации HTML5: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

Атрибуты defer и async должны не указывается, если атрибут src нет.


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


Точные детали обработки для этих атрибуты, в основном исторические причины, несколько нетривиальные, с участием ряда аспектов HTML. Требования к реализации поэтому по необходимости разбросал по всей спецификации. алгоритмы ниже (в этом разделе) опишите ядро ​​этой обработки, но эти алгоритмы ссылки и на которые ссылаются правила синтаксического анализа для теги начала и конца скрипта в HTML, в иностранный контент, а в XML правила для метода document.write () обработка скриптов и т. д.


Если элемент имеет атрибут src, и элемент имеет атрибут defer, и элемент был помечен как «парсер вставлен», и элемент не имеет асинхронного атрибута:

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

155 голосов
/ 24 мая 2012

Реальный ответ: потому что вы не можете доверять отсрочке.

В концепции отсрочка и асинхронность отличаются следующим образом:

async позволяет загружать скрипт в фоновом режиме без блокировки. Затем, в момент завершения загрузки, рендеринг блокируется, и этот скрипт выполняется. Рендеринг возобновляется после выполнения скрипта.

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

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

  • В некоторых ситуациях в некоторых браузерах есть ошибка, из-за которой defer скрипты работают не по порядку.
  • Некоторые браузеры задерживают событие DOMContentLoaded до окончания загрузки скриптов defer, а некоторые - нет.
  • Некоторые браузеры подчиняются defer элементам <script> со встроенным кодом и без атрибута src, а некоторые игнорируют его.

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

<script defer async src="..."></script>

98% браузеров, используемых по всему миру, и 99% в США избежат блокировки при таком подходе.

(Если вам нужно дождаться окончания разбора документа, прослушайте событие DOMContentLoaded или воспользуйтесь удобной функцией .ready() jQuery. В любом случае вы захотите сделать это, чтобы изящно использовать браузеры, которые не Реализовать defer вообще.)

50 голосов
/ 09 марта 2011

ОБНОВЛЕНО: 19.02.2016

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


По сути, defer указывает браузеру подождать "пока он не готов", прежде чем выполнять JavaScript в этом блоке сценария.Обычно это происходит после завершения загрузки DOM и document.readyState == 4

Атрибут defer относится только к Internet Explorer.В Internet Explorer 8 в Windows 7 результат, который я вижу на вашей тестовой странице JS Fiddle: 1 - 2 - 3.

Результаты могут различаться в зависимости от браузера.

http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx

Вопреки распространенному мнению, IE следует стандартам чаще, чем допускают люди, в действительности атрибут defer определен в спецификации DOM Level 1 http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html

W3Cопределение отсрочки: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer:

"Когда установлено, этот логический атрибут дает подсказку агенту пользователя, что скрипт не собирается генерировать какое-либо содержимое документа (например, нет« document.write »в javascript)и, следовательно, пользовательский агент может продолжить анализ и рендеринг. "

13 голосов
/ 16 октября 2013

defer можно использовать только в теге <script> для включения внешнего сценария . Следовательно, рекомендуется использовать в тегах <script> в разделе <head>.

7 голосов
/ 05 сентября 2015

Поскольку атрибут defer работает только с тегом scripts с src.Нашел способ подражать defer для встроенных скриптов.Используйте событие DOMContentLoaded.

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

Это связано с тем, что событие DOMContentLoaded вызывается после полной загрузки атрибутов с отложенной привязкой.

6 голосов
/ 08 мая 2013

Атрибут defer предназначен только для внешних скриптов (должен использоваться только при наличии атрибута src).

4 голосов
/ 20 января 2016

Взгляните на эту превосходную статью Глубокое погружение в темные воды загрузки скриптов от разработчика Google Джейка Арчибальда, написанного в 2013 году.

Цитирую соответствующий раздел из этой статьи:

Отсрочка

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

Спецификация говорит : загрузка вместе, выполнение по порядку непосредственно перед DOMContentLoaded.Игнорировать «defer» в сценариях без «src».

IE <10 говорит </strong>: я мог бы выполнить 2.js в середине выполнения 1.js.Разве это не весело ??

Браузеры в красном говорят : я понятия не имею, что это за «отсрочка», я собираюсь загрузитьсценариев, как если бы их не было.

Другие браузеры говорят: : Хорошо, но я не могу игнорировать «defer» в сценариях без «src».

(добавлю, что ранние версии Firefox запускают DOMContentLoaded до завершения работы сценариев defer, согласно этому комментарию .)

Современные браузеры, кажется,async поддерживается должным образом, но вы должны быть в порядке со сценариями, работающими не по порядку и, возможно, до DOMContentLoaded.

4 голосов
/ 10 октября 2013

Следует также отметить, что могут быть проблемы в IE <= 9 при использовании <code>script defer в определенных ситуациях.Подробнее об этом: https://github.com/h5bp/lazyweb-requests/issues/42

1 голос
/ 18 февраля 2013

Этот логический атрибут установлен для указания браузеру, что сценарий должен выполняться после анализа документа. Поскольку эта функция еще не была реализована во всех других основных браузерах, авторы не должны предполагать, что выполнение сценария будет фактически отложено. Никогда не вызывайте document.write () из отложенного скрипта (начиная с Gecko 1.9.2, это уничтожит документ). Атрибут defer не должен использоваться в сценариях, которые не имеют атрибута src. Начиная с Gecko 1.9.2, атрибут defer игнорируется в сценариях, которые не имеют атрибута src. Однако в Gecko 1.9.1 даже встроенные сценарии откладываются, если установлен атрибут defer.

отложено работает с Chrome, Firefox, т. Е.> 7 и Safari

ref: https://developer.mozilla.org/en-US/docs/HTML/Element/script

0 голосов
/ 01 июля 2016

Атрибут defer является логическим атрибутом.

При наличии он указывает, что скрипт выполняется после завершения анализа страницы.

Примечание. Атрибут defer предназначен только для внешних скриптов(должен использоваться только при наличии атрибута src).

Примечание. Существует несколько способов выполнения внешнего сценария:

Если присутствует асинхронный: сценарий выполняется асинхронно состальная часть страницы (сценарий будет выполняться, пока страница продолжает синтаксический анализ) Если асинхронизация отсутствует и присутствует отсрочка: сценарий выполняется после завершения анализа страницы, если отсутствует асинхронизация или отсрочка: сценарий извлекается ивыполняется немедленно, прежде чем браузер продолжит синтаксический анализ страницы

...