Подтвердить что ты не робот

Когда происходит разбор дерева HTML DOM?

Я всегда вижу поток рендеринга для веб-страницы, как показано на следующем рисунке: введите описание изображения здесь

Итак, картина начинается только после разбора дерева DOM и создания CSSOM, правильно? Другое высказывание состоит в том, что положить <script> в конце <body> является лучшей практикой, так чтобы страница отображала что-то до загрузки script.

Мой вопрос: когда происходит разбор дерева DOM и как мы можем сказать, что это сделано? По моему мнению, <script>, в конце концов, также является частью дерева DOM, и только если загружен script, мы можем вызвать дерево DOM. Браузер читает html файл сверху вниз, создавая дерево DOM, и когда он видит <script>, он останавливается, чтобы загрузить и выполнить его, пока синтаксический анализ не пройдет через всю страницу. Или же страница рисует страницу одновременно с разбором дерева DOM?

4b9b3361

Ответ 1

TL; DR: после получения документа начинается синтаксический анализ мгновенно.

Разбор и живопись

Для более подробного объяснения нам нужно погрузиться в способ работы рендеринга.

Rendering engine анализирует HTML-документ и создает два дерева: content tree и render tree. Дерево содержимого содержит все узлы DOM. Дерево рендеринга содержит всю информацию о стиле (CSSOM) и только узлы DOM, которые необходимы для отображения страницы te.

Как только дерево рендеринга было создано, браузеры проходят через два процесса: применение layout и painting каждого DOM node. Применение макета означает вычисление точных координат, где DOM node должен появиться на экране. Живопись означает фактическое отображение пикселей и применение стилистических свойств.

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

Вы можете видеть, как этот процесс происходит в вашем браузере. Например, откройте инструменты разработчика Chrome и загрузите сайт по вашему выбору.

вкладка

После записи активности на вкладке Network вы заметите, что при загрузке документа начинается синтаксический анализ. Он распознает ресурсы и начинает их загружать. Синяя вертикальная линия указывает событие DOMContentLoaded, а красная вертикальная линия указывает на событие load.

вкладка временной шкалы

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

Одиночная резьба

Механизм рендеринга одинарный. В этом потоке происходит почти все, кроме сетевых операций.

Объедините это с синхронным характером сети. Разработчики ожидают, что <script> будет разобран и выполнен немедленно (то есть: как только парсер достигнет тега script). Это означает, что:

  • Ресурс должен быть извлечен из сети (это может быть медленный процесс из-за поиска DNS и скорости соединения).
  • Содержимое ресурса передается интерпретатору Javascript.
  • Интерпретатор анализирует и выполняет код.

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

Можно обойти эту проблему, отметив ресурс defer и/или async. async загружает файл во время разбора HTML и приостанавливает парсер HTML, чтобы выполнить его, когда он закончил загрузку. defer загружает файл во время разбора HTML и выполняет его только после завершения анализатора.

Спекулятивный разбор

Некоторые браузеры стремятся обойти блокирующий аспект <script> с помощью так называемого спекулятивного разбора. Двигатель анализирует вперед (и запускает построение дерева HTML!), В то время как скрипты загружаются и выполняются. Firefox и Chrome используют эту технику.

Вы можете представить себе прирост производительности, если спекуляция завершается успешно (например, DOM не был изменен сценариями, включенными в документ). Ожидание выполнения сценариев не было необходимо, и страница была успешно раскрашена. Недостатком является то, что больше работы теряется, когда спекуляция терпит неудачу.

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

// Results in an unbalanced tree
<script>document.write("<div>");</script>

// Results in an unfinished token
<script>document.write("<div></div");</script>

Дальнейшее чтение

Следующие ресурсы стоят времени на чтение:

Ответ 2

Другое высказывание: размещение <script> в конце <body> - лучшая практика, так что страница отображает что-то до загрузки script.

Основная причина размещения тега script в конце тега body: скачать и выполнить. JavaScripts блокирует разбор HTML (или, вы можете сказать, что они просто части разбора). Если они помещены в <head>, пользователь может долго ждать, пока не увидит что-либо на веб-странице. У вас есть html-страница:

<html>
  <head>
    <!-- this huge.js takes 10 seconds to download -->
    <script src="huge.js"></script>
  </head>
  <body>
    <div>
      My most fancy div!
    </div>
  </body>
</html>

// huge.js
(function () {
   // Some CPU intensive JS operations which take 10 second to complete
})();

Браузер начнет выполнять JSP с интенсивным процессором сразу после того, как он достигнет тега <script>. И он будет блокировать, анализируя остальную часть содержимого HTML. Таким образом, в этом случае пользователь не сможет увидеть свой любимый div до того, как JavaScript будет загружен и выполнен (всего 20 секунд).

Вы можете использовать DOMContentLoaded, чтобы определить, загружен ли исходный DOM и проанализирован ли он. И ваше утверждение в последнем абзаце совершенно правильно: каждый раз, когда парсер HTML видит <script>, он будет загружать и выполнять его синхронно (см. Уведомление 2). В конце концов <script> выполняются, и весь HTML обрабатывается, DOMContentLoaded будет запущен.

Примечание 1: DOMContentLoaded НЕ будет ждать CSS и изображений

Примечание 2. В большинстве браузеров есть функция "Спекулятивный разбор". Если есть несколько файлов JavaScript, они будут загружаться одновременно. Тем не менее, они будут по-прежнему выполняться последовательно по основному потоку.

За последний вопрос:

Или страница страниц одновременно обрабатывает страницу дерева DOM?

Из моего собственного понимания ответ ДА, браузер попытается нарисовать ASAP. Иными словами, механизм рисования не ждет, пока дерево рендеринга будет полностью готово. Поэтому для обработки краски должна быть отдельная нить.

Не стесняйтесь исправлять меня, если какое-либо из моих понятий неверно:)

Литература:

Ответ 3

Фактически это зависит от браузера относительно конкретного порядка загрузки всего, но для разбора DOM он работает сверху вниз. Парсер перемещает ветвь по ветки, поэтому, когда она встречает голову, она будет перемещаться по каждому ребенку. Если у элемента есть дочерний элемент, он затем переместится на дочерний элемент/дочерний элемент, прежде чем перемещаться по дереву. Чтобы поместить это в очень простой псевдокод:

while DOM != parsed:
    if current_node.has_child():
        current_node = child_node
        execute_node()
    elif current_node.has_sibling():
        current_node = sibling_node
        execute_node()
    elif current_node.has_parent_sibling():
        current_node = parent_sibling
        execute_node()
    else:
        current_node = parent_node

В основном он касается тегов script/link в качестве родительских узлов, инициирует запрос HTTP/S GET, если он является внешним файлом и анализирует код, прежде чем перейти к следующему node. Поэтому причина, по которой мы используем теги script в конце, состоит в том, что они обычно не используются при загрузке страницы, а обрабатывают вещи после ее загрузки. Таким образом, консенсус заключается в том, что лучше получить что-то на странице, а затем загрузить свой JS позже, чтобы он мог справиться с такой важной анимацией, которую вы имеете в пункте меню.

Конечно, это исключение, при котором вы можете указать парсеру DOM для выполнения сценариев асинхронно - синтаксический анализатор создает дополнительный поток для анализа запроса JS или отсрочки - GET, но файл не анализируется до тех пор, пока HTML-документ завершен синтаксический анализ.