Defer не загружает страницу быстрее - PullRequest
0 голосов
/ 31 января 2019

Я пытаюсь понять отсрочку на простом примере.У меня есть приведенный ниже код JS, выполнение которого занимает 5 секунд.

home.js

function wait(ms){
    var start = new Date().getTime();
    var end = start;
    while(end < start + ms) {
      end = new Date().getTime();
   }

 }

 wait(5000);

Я использую его в home.html как показано ниже:

<html>
    <head>
        <script defer src="home.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
</body>
</html>

Я обнаружил, что если использовать defer или поместить мой скрипт в конец тега body (в моем понимании оба они эквивалентны), hello world появляется только через 5 секунд.Но если я использую asnyc, «Hello World» появляется сразу.Почему это?

Ответы [ 4 ]

0 голосов
/ 31 января 2019

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

Page load animation

Так что вы можете использовать defer или async в зависимости от ваших требований .

Однако похоже, что Chrome начинает рисовать страницу до или после выполнения JavaScript в зависимости от того, была ли страница загружена по сети или из файловой системы:

Сеть:

Loaded over http:

Файловая система:

Loaded over file:

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

0 голосов
/ 31 января 2019

Давайте посмотрим на это (откройте консоль отладки, чтобы увидеть вывод):

test.js

document.addEventListener("DOMContentLoaded", function(event) {
    console.log("[4]: 'DOMContentLoaded' event fired!");
});

function wait(ms){
    console.log("[2]: 'wait' started!");
    var start = new Date().getTime();
    var end = start;
    while(end < start + ms) {
        end = new Date().getTime();
    }
    console.log("[3]: 'wait' finished!");
}

wait(5000);

test.html

<html>
    <head>
        <script defer src="test.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
    <script>console.log('[1]: DOM is parsed!')</script>
</body>
</html>

Журнал консоли:

[1]: DOM is parsed!
[2]: 'wait' started!
[3]: 'wait' finished!
[4]: 'DOMContentLoaded' event fired!

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

  1. DOM загружен и проанализирован.
  2. Функция wait запускается (потому что она deferred и выполняется после анализа DOM, но до того, как событие DOMContentLoaded запущено).
  3. Сценарий 'defer` завершен.
  4. Событие DOMContentLoaded вызывается (только после того, как все сценарии defer выполнены в порядке их следования в HTML).
  5. HTML отображается, но некоторые ресурсы, такие как таблицы стилей и изображения, все еще могут загружаться.

Время рендеринга DOM может быть различным в разных браузерах и способах размещения HTML-файла: в Chrome (как показали мои тесты) в случае http:// DOM отображается раньше defer сценарии выполняются, но в случае file:/// DOM отображается после запуска defer сценариев и завершения их синхронного кода.

Сводка(из вашего вопроса в комментариях):

  • defer и async скрипты загружаются асинхронно.
  • defer скрипты будут выполняться в порядке вкоторые они помещают в HTML.
  • defer сценарии будут выполняться после загрузки и анализа DOM, но до запуска DOMContentLoaded.
  • *Скрипт 1069 * может быть загружен в любое время и запущен в любом порядке (скорее всего, в порядке загрузки).Если такой скрипт загружается до анализа DOM, вы не можете ожидать, что этот скрипт сможет найти некоторые элементы в HTML.Кроме того, в этом случае браузер не ожидает загрузки и выполнения сценария для запуска события DOMContentLoaded.
0 голосов
/ 31 января 2019

@ Сергей отвечает хорошо, но на самом деле не объясняет.Принципиальная схема 4.12.1 здесь показывает, что когда указано defer, браузер ожидает начала выполнения скрипта, пока весь HTML не будет проанализирован .Ключ разбора не рендеринг.Рендеринг начинается в то же время , что и выполнение отложенного сценария, но в этом конкретном случае сценарий блокируется в тяжелом цикле.Если функция wait() не блокируется, рендеринг может эффективно обновить DOM сразу после завершения анализа и параллельно с неблокирующим отложенным выполнением сценария.DOMContentLoaded все равно будет запущен, когда скрипт завершит работу.

function wait(ms){
  return new Promise((resolve, reject) => {setTimeout(() => resolve(), ms)});
}

 wait(3000).then(() => console.log('done'));
<html>
    <head>
        <script defer src="home.js"></script>
    </head>
<body>
    <h1>Hello World!</h1>
</body>
</html>
0 голосов
/ 31 января 2019

Чтобы ответить на ваш ближайший вопрос, поможет документация Script Element для MDN.

Отсрочка:

Скрипты с атрибутом defer предотвратят DOMContentLoadedсобытие от запуска до тех пор, пока скрипт не загрузится и не завершит оценку.

Async:

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

Асинхронный сценарий не удерживает событие DOMContentLoaded от запуска.

Существует несколько более эффективных способов заставить контент dom появляться после задержки, который не будет блокировать поток пользовательского интерфейса.как ты делаешь;Это очень плохая практика.

Извлечение из DOMContentLoaded :

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

Обычной практикой будет добавление содержимого DOM после загрузки документа.Вы все еще можете сделать это с таймаутом, но обычно дополнительный контент DOM будет загружен после какого-то другого асинхронного действия , как ответ, полученный от fetch .

Вотпример добавления содержимого dom после задержки:

function wait(ms){
  window.setTimeout(function() {
    var element = document.createElement("h1")
    element.innerText = "Hello World!"
    document.body.appendChild(element)
  }, ms)
}

 wait(5000);
<html>
    <head>
        <script src="home.js"></script>
    </head>
<body>
    
</body>
</html>
...