← Браузер/Как браузер строит страницу#175 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: async и сетьТермин: Event LoopТермин: Core Web Vitals

Как браузер строит страницу

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

Архитектура браузера

Современный браузер — сложная система из нескольких компонентов:

  • UI браузера — адресная строка, кнопки навигации, вкладки
  • Движок рендеринга — строит страницу из HTML/CSS (Blink в Chrome/Edge, Gecko в Firefox, WebKit в Safari)
  • JavaScript-движок — выполняет JS (V8 в Chrome, SpiderMonkey в Firefox)
  • Сетевой слой — обрабатывает HTTP-запросы
  • Хранилище данных — cookies, localStorage, IndexedDB
  • Каждая вкладка в современном браузере — отдельный процесс операционной системы. Это изоляция безопасности: если одна вкладка зависнет, остальные продолжат работу.

    Парсинг HTML → DOM

    Движок рендеринга читает HTML-текст символ за символом. Токенайзер превращает текст в токены (<div, class="container", >), а парсер строит из них дерево объектов — DOM (Document Object Model).

    DOM — это не HTML-текст, а живое дерево объектов в памяти. Каждый тег становится узлом. Вот почему document.getElementById('app') работает мгновенно — браузер уже построил это дерево.

    Парсинг HTML устойчив к ошибкам. Если ты забудешь закрыть тег, браузер исправит ошибку автоматически. Это отличает HTML от XML, который при ошибке просто отказывает в парсинге.

    Парсинг CSS → CSSOM

    Одновременно с HTML браузер парсит CSS и строит CSSOM (CSS Object Model) — дерево стилей. CSSOM учитывает каскадность, специфичность и наследование. В конечном объекте стилей каждый элемент содержит все применимые к нему правила.

    CSS блокирует рендеринг: браузер не начнёт отрисовку до тех пор, пока не загрузит и не обработает весь CSS. Поэтому CSS-файлы нужно подключать в <head>, а не в конце страницы.

    Render Tree = DOM + CSSOM

    Браузер объединяет DOM и CSSOM в Render Tree — дерево только видимых элементов с вычисленными стилями. Элементы с display: none в Render Tree не попадают. visibility: hidden — попадают, но остаются невидимыми.

    Layout (Reflow)

    Браузер обходит Render Tree и вычисляет точное положение и размер каждого элемента в пикселях. Этот этап называется Layout или Reflow. Он учитывает viewport, box model, float, flexbox, grid — всю CSS-геометрию.

    Paint

    Браузер проходит по вычисленной геометрии и заполняет пиксели: рисует текст, цвета, изображения, тени, border-radius. Это происходит по слоям — некоторые элементы (с transform, opacity, will-change) выносятся на отдельные GPU-слои.

    Composite

    Готовые слои отправляются на GPU, который склеивает их в финальное изображение. GPU-операции (transform, opacity) не требуют layout и paint — это делает их идеальными для анимаций.

    readyState и события загрузки

    document.readyState отражает текущую стадию загрузки:

  • "loading" — HTML ещё парсится
  • "interactive" — DOM построен, ресурсы (картинки, стили) ещё грузятся. Срабатывает событие DOMContentLoaded
  • "complete" — всё загружено. Срабатывает событие load
  • Понимание разницы между DOMContentLoaded и load критично для правильной инициализации скриптов.

    Примеры

    Измерение этапов загрузки страницы и изменений readyState

    // Отслеживаем изменения readyState
    // В реальном коде этот скрипт должен быть в <head>
    console.log('Начальный readyState:', document.readyState)
    
    document.addEventListener('readystatechange', () => {
      const state = document.readyState
      const time = performance.now().toFixed(1)
      console.log(`readyState изменился: "${state}" — через ${time} мс`)
    })
    
    // DOMContentLoaded — DOM готов, можно работать с элементами
    // Срабатывает раньше load
    document.addEventListener('DOMContentLoaded', () => {
      const timing = performance.now()
      console.log(`DOMContentLoaded: ${timing.toFixed(1)} мс`)
      // Теперь можно безопасно обращаться к DOM-элементам
      const allElements = document.querySelectorAll('*').length
      console.log(`Элементов в DOM: ${allElements}`)
    })
    
    // load — всё загружено: картинки, стили, шрифты
    window.addEventListener('load', () => {
      const timing = performance.now()
      console.log(`load: ${timing.toFixed(1)} мс`)
    
      // Navigation Timing API — детальная статистика загрузки
      const nav = performance.getEntriesByType('navigation')[0]
      console.log('DNS:', (nav.domainLookupEnd - nav.domainLookupStart).toFixed(1), 'мс')
      console.log('DOM парсинг:', (nav.domContentLoadedEventStart - nav.responseEnd).toFixed(1), 'мс')
      console.log('Полная загрузка:', nav.loadEventEnd.toFixed(1), 'мс')
    })

    Как браузер строит страницу

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

    Архитектура браузера

    Современный браузер — сложная система из нескольких компонентов:

  • UI браузера — адресная строка, кнопки навигации, вкладки
  • Движок рендеринга — строит страницу из HTML/CSS (Blink в Chrome/Edge, Gecko в Firefox, WebKit в Safari)
  • JavaScript-движок — выполняет JS (V8 в Chrome, SpiderMonkey в Firefox)
  • Сетевой слой — обрабатывает HTTP-запросы
  • Хранилище данных — cookies, localStorage, IndexedDB
  • Каждая вкладка в современном браузере — отдельный процесс операционной системы. Это изоляция безопасности: если одна вкладка зависнет, остальные продолжат работу.

    Парсинг HTML → DOM

    Движок рендеринга читает HTML-текст символ за символом. Токенайзер превращает текст в токены (<div, class="container", >), а парсер строит из них дерево объектов — DOM (Document Object Model).

    DOM — это не HTML-текст, а живое дерево объектов в памяти. Каждый тег становится узлом. Вот почему document.getElementById('app') работает мгновенно — браузер уже построил это дерево.

    Парсинг HTML устойчив к ошибкам. Если ты забудешь закрыть тег, браузер исправит ошибку автоматически. Это отличает HTML от XML, который при ошибке просто отказывает в парсинге.

    Парсинг CSS → CSSOM

    Одновременно с HTML браузер парсит CSS и строит CSSOM (CSS Object Model) — дерево стилей. CSSOM учитывает каскадность, специфичность и наследование. В конечном объекте стилей каждый элемент содержит все применимые к нему правила.

    CSS блокирует рендеринг: браузер не начнёт отрисовку до тех пор, пока не загрузит и не обработает весь CSS. Поэтому CSS-файлы нужно подключать в <head>, а не в конце страницы.

    Render Tree = DOM + CSSOM

    Браузер объединяет DOM и CSSOM в Render Tree — дерево только видимых элементов с вычисленными стилями. Элементы с display: none в Render Tree не попадают. visibility: hidden — попадают, но остаются невидимыми.

    Layout (Reflow)

    Браузер обходит Render Tree и вычисляет точное положение и размер каждого элемента в пикселях. Этот этап называется Layout или Reflow. Он учитывает viewport, box model, float, flexbox, grid — всю CSS-геометрию.

    Paint

    Браузер проходит по вычисленной геометрии и заполняет пиксели: рисует текст, цвета, изображения, тени, border-radius. Это происходит по слоям — некоторые элементы (с transform, opacity, will-change) выносятся на отдельные GPU-слои.

    Composite

    Готовые слои отправляются на GPU, который склеивает их в финальное изображение. GPU-операции (transform, opacity) не требуют layout и paint — это делает их идеальными для анимаций.

    readyState и события загрузки

    document.readyState отражает текущую стадию загрузки:

  • "loading" — HTML ещё парсится
  • "interactive" — DOM построен, ресурсы (картинки, стили) ещё грузятся. Срабатывает событие DOMContentLoaded
  • "complete" — всё загружено. Срабатывает событие load
  • Понимание разницы между DOMContentLoaded и load критично для правильной инициализации скриптов.

    Примеры

    Измерение этапов загрузки страницы и изменений readyState

    // Отслеживаем изменения readyState
    // В реальном коде этот скрипт должен быть в <head>
    console.log('Начальный readyState:', document.readyState)
    
    document.addEventListener('readystatechange', () => {
      const state = document.readyState
      const time = performance.now().toFixed(1)
      console.log(`readyState изменился: "${state}" — через ${time} мс`)
    })
    
    // DOMContentLoaded — DOM готов, можно работать с элементами
    // Срабатывает раньше load
    document.addEventListener('DOMContentLoaded', () => {
      const timing = performance.now()
      console.log(`DOMContentLoaded: ${timing.toFixed(1)} мс`)
      // Теперь можно безопасно обращаться к DOM-элементам
      const allElements = document.querySelectorAll('*').length
      console.log(`Элементов в DOM: ${allElements}`)
    })
    
    // load — всё загружено: картинки, стили, шрифты
    window.addEventListener('load', () => {
      const timing = performance.now()
      console.log(`load: ${timing.toFixed(1)} мс`)
    
      // Navigation Timing API — детальная статистика загрузки
      const nav = performance.getEntriesByType('navigation')[0]
      console.log('DNS:', (nav.domainLookupEnd - nav.domainLookupStart).toFixed(1), 'мс')
      console.log('DOM парсинг:', (nav.domContentLoadedEventStart - nav.responseEnd).toFixed(1), 'мс')
      console.log('Полная загрузка:', nav.loadEventEnd.toFixed(1), 'мс')
    })

    Задание

    Напиши код, который фиксирует момент начала выполнения скрипта и измеряет, сколько времени прошло до DOMContentLoaded и до load. Выведи результаты в консоль в виде: "DOMContentLoaded через X мс" и "load через Y мс".

    Подсказка

    Событие DOMContentLoaded слушается на document, событие load — на window. Время разницы: performance.now() минус startTime, записанный до регистрации слушателей.

    Загружаем среду выполнения...
    Загружаем AI-помощника...