И так, я как разработчик собираюсь создавать быстрые и надежные веб-сайты, для этого мне нужно понимать механику каждого шага, который браузер выполняет для отображения веб-страницы, чтобы каждый шаг мог быть рассмотрен и оптимизирован во время разработки. Этот пост представляет собой краткое изложение фактов о этом процессе.
Когда браузер получает HTML страницу из сети, он парсит HTML в Document Object Model (DOM). Разбивает HTML на токены, которы представляют собой начальные теги, конечные теги и их содержимое . Из этого браузер строит DOM.
Когда парсер встречает внешний ресурс, такой как файл CSS или JavaScript, он останавливается, чтобы запросить эти файлы. Парсер продолжит работу когда CSS начнёт загружаться, хотя он будет блокировать рендеринг, пока он не будет загружен и пропаршен (подробнее об этом чуть позже).
Файлы JavaScript немного отличаются - по умолчанию они блокируют парсинг HTML, пока файл JavaScript загружается, а затем парсится. Есть два атрибута, которые можно добавить в теги сценария, чтобы улучшить это: defer
и async
. Оба позволяют парсеру продолжать работу, пока файл JavaScript загружается в фоновом режиме, но они работают по-разному в том, как они выполняются. Подробнее об этом:
defer
означает, что выполнение файла будет отложено до завершения парсинга документа. Если несколько файлов имеют атрибут defer, они будут выполняться в том порядке, в котором они были обнаружены в HTML.
<script type="text/javascript" src="script.js" defer>
async
означает, что файл будет выполнен, как только он загрузится, что может быть во время или после процесса парсинга, и поэтому порядок, в котором выполняются асинхронные сценарии, не может быть гарантирован.
<script type="text/javascript" src="script.js" async>
Кроме того, современные браузеры будут продолжать сканировать HTML-код, пока он заблокирован, и "смотреть" на то, какие внешние ресурсы определены дальше, а затем загружать их. То, как браузеры это делают, варьируется в зависимости от браузера, поэтому нельзя полагаться на то, что они будут вести себя определенным образом. Чтобы пометить ресурс как важный и, следовательно, с большей вероятностью быть загруженным на ранней стадии процесса рендеринга, можно использовать тег ссылки с rel="preload".
<link href="style.css" rel="preload" as="style" />
CSSOM - это карта всех селекторов CSS и соответствующих свойств для каждого селектора в форме дерева с корневым узлом, родственником, потомком, дочерним элементом и другими отношениями. CSSOM очень похож на объектную модель документа (DOM). Оба они являются частью пути рендеринга, который представляет собой серию шагов, которые должны произойти для правильного рендеринга веб-сайта.
CSSOM вместе с DOM необходим для построения дерева рендеринга, которое, в свою очередь, используется браузером для компоновки и отрисовки веб-страницы.
Подобно HTML и DOM, когда файлы CSS загружаются, они должны быть пропаршены и преобразованы в дерево - на этот раз CSSOM. Он описывает все селекторы CSS на странице, их иерархию и их свойства.
CSSOM отличается от DOM, тем, что он не может быть построен постепенно, поскольку правила CSS могут перезаписывать друг друга в из-за специфичности. Вот почему CSS блокирует рендеринг, поскольку до тех пор, пока весь CSS не будет проанализирован и не будет построен CSSOM, браузер не может знать, где и как разместить каждый элемент на экране.
После того, как синхронно загруженный JavaScript и DOM будут полностью спаршены и готовы, будет сгенерировано событие document.DOMContentLoaded. Для любых сценариев, которым требуется доступ к модели DOM, например, для управления ею или прослушивания событий взаимодействия с пользователем, рекомендуется сначала дождаться этого события перед выполнением сценариев.
document.addEventListener('DOMContentLoaded', (event) => {
// You can now safely access the DOM
});
А после того, как все остальное, например асинхронный JavaScript, изображения и т.д., завершили загрузку, запускается событие window.load.
window.addEventListener('load', (event) => {
// The page has now fully loaded
});
Рендер дерево представляет собой сочетание DOM и CSSOM, а так же то, что будет отрендерено на страницу. Это не обязательно означает, что все узлы в дереве рендеринга будут визуально присутствовать, например узлы со стилями opacity: 0
или visibility: hidden
будут включены, и все еще могут быть прочитаны средством чтения с экрана и т.д., тогда как те, которые установлены на display: none
, не будут включены. Кроме того, такие теги, как <head>
не содержащие визуальной информации, всегда будут пропущены.
Как и в случае с движками JavaScript, разные браузеры имеют разные механизмы рендеринга.
Теперь, когда у нас есть полное дерево рендеринга, браузер знает, что рендерить, но не знает, где рендерить. Следовательно, необходимо рассчитать макет страницы (т.е. положение и размер каждого узла). Механизм рендеринга проходит дерево рендеринга, с вершины вниз, вычисляя координаты, в которых должен отображаться каждый узел.
Как только это будет сделано, последний шаг - взять эту информацию о макете и отрисвать пиксели на экране.
И вуаля! В конце концов, у нас есть полностью отрисованная веб-страница!