← Собеседование/Как браузер загружает страницу?#366 из 383← ПредыдущийСледующий →+40 XP
Полезно по теме:Маршрут: подготовка к интервьюГайд: портфолио juniorГайд: карьерный планТермин: Closure
← НазадДалее →

Как браузер загружает страницу?

Краткий ответ

Загрузка страницы — это многоступенчатый процесс: сначала браузер устанавливает сетевое соединение (DNS + TCP), получает HTML и парсит его в DOM-дерево, параллельно строит CSSOM из стилей, затем объединяет их в Render Tree, вычисляет расположение элементов (Layout), рисует пиксели (Paint) и выводит результат на экран (Compositing). Понимание этого процесса критично для оптимизации производительности сайта.

Полный разбор

Этап 1: Сеть

[Браузер] → DNS lookup → IP-адрес сервера
         → TCP Handshake (SYN → SYN-ACK → ACK)
         → TLS Handshake (для HTTPS)
         → HTTP GET /index.html
         ← HTTP 200 OK + тело HTML

DNS lookup — перевод домена (example.com) в IP-адрес. Результат кешируется.

TCP соединение — три рукопожатия перед передачей данных.

TTFB (Time To First Byte) — время от запроса до первого байта ответа. Важная метрика.

Этап 2: Парсинг HTML → DOM

Браузер читает HTML сверху вниз и строит DOM-дерево (Document Object Model):

HTML-текст → Tokenizer → Parser → DOM Tree

<html>
  <head>...</head>
  <body>
    <div id="app">
      <p>Hello</p>
    </div>
  </body>
</html>

      Document
         │
        html
       /    \
    head    body
              │
             div#app
              │
              p
              │
           "Hello"

Этап 3: Парсинг CSS → CSSOM

Параллельно с DOM строится CSSOM (CSS Object Model). CSS — блокирующий ресурс для рендеринга: браузер не начнёт отрисовку, пока не загрузит и не распарсит все CSS-файлы в <head>.

Этап 4: JavaScript — блокировка парсинга

Синхронный <script> блокирует парсинг HTML:

HTML парсится → встретили <script src="app.js"> →
СТОП парсинга → скачать app.js → выполнить →
ПРОДОЛЖИТЬ парсинг HTML

Почему? Скрипт может вызвать document.write() и полностью изменить HTML.

async и defer убирают блокировку:

Обычный:  HTML ███░░░░░░███  (░ = пауза на скрипт)
async:    HTML ████████████  (скрипт скачивается параллельно, выполняется сразу)
defer:    HTML ████████████  (скрипт скачивается параллельно, выполняется после парсинга)

| Атрибут | Скачивание | Выполнение | Порядок |

|---------|-----------|-----------|---------|

| (нет) | блокирует | сразу | да |

| async | параллельно | как скачается | нет |

| defer | параллельно | после парсинга HTML | да |

Этап 5: Render Tree

DOM + CSSOM объединяются в Render Tree — только видимые элементы с применёнными стилями. Элементы с display: none не попадают в Render Tree.

Этап 6: Layout (Reflow)

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

Этап 7: Paint

Браузер рисует пиксели: цвета, тексты, изображения, тени. Каждый слой рисуется отдельно.

Этап 8: Compositing

GPU объединяет слои в финальное изображение на экране.

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

// DOMContentLoaded — HTML распарсен, DOM готов
// Скрипты с defer выполнились. Картинки могут ещё грузиться.
document.addEventListener('DOMContentLoaded', () => {
  console.log('DOM готов — можно работать с элементами')
})

// load — ВСЁ загружено: картинки, стили, скрипты
window.addEventListener('load', () => {
  console.log('Страница полностью загружена')
})

Правило: используй DOMContentLoaded для инициализации UI. Используй load только когда нужны размеры картинок.

Критический путь рендеринга (Critical Rendering Path)

Минимальная цепочка для первой отрисовки страницы:

HTML → DOM → Render Tree → Layout → Paint
       CSS → CSSOM ↗

Оптимизация CRP:

  • CSS — в <head> (чтобы не блокировать рендеринг после парсинга)
  • JS — в конце <body> или с defer
  • Минимизировать блокирующие ресурсы
  • Использовать <link rel="preload"> для критических ресурсов
  • Связанные уроки курса

  • DOM — работа с деревом документа
  • Жизненный цикл страницы (DOMContentLoaded, load, beforeunload)
  • async и defer скрипты
  • Как отвечать на собеседовании

    Начни с общей картины: "Загрузка страницы проходит в несколько этапов: сеть, парсинг, рендеринг". Это показывает системное мышление.

    Обязательно упомяни: блокирующий характер синхронных скриптов и CSS — это ключевой момент для оптимизации. Разницу между DOMContentLoaded и load события.

    Хорошее дополнение: упомяни Critical Rendering Path и что ты делал бы для оптимизации (defer, preload, CSS в head).

    Время ответа: 2-3 минуты. Можно нарисовать схему на доске.

    Красные флаги ответа

    1. "Браузер просто открывает страницу" — полное непонимание процесса. Это показывает, что кандидат никогда не занимался оптимизацией.

    2. Путаница DOMContentLoaded и load — очень частая ошибка, из-за которой возникают баги с "элемент не найден".

    3. Незнание про блокирующие скрипты — если не знаешь, почему <script> в <head> без defer замедляет загрузку, это серьёзный пробел.

    Примеры

    Симуляция последовательности загрузки браузера с временными метками

    // Симуляция этапов загрузки браузера
    // В реальности эти этапы выполняет движок браузера
    
    function simulateBrowserLoading(url) {
      const steps = []
      const startTime = Date.now()
    
      function log(step, detail = '') {
        const elapsed = Date.now() - startTime
        steps.push({ step, detail, ms: elapsed })
        console.log(`[${String(elapsed).padStart(4)}ms] ${step}${detail ? ': ' + detail : ''}`)
      }
    
      console.log(`=== Загрузка ${url} ===\n`)
    
      // 1. Сеть
      log('DNS lookup', 'example.com → 93.184.216.34')
      log('TCP Handshake', 'SYN → SYN-ACK → ACK')
      log('TLS Handshake', 'HTTPS шифрование')
      log('HTTP GET', `GET / HTTP/1.1 Host: ${url}`)
      log('HTTP 200 OK', 'Получен HTML (12 KB)')
    
      // 2. Парсинг
      log('HTML Parsing', 'Построение DOM-дерева...')
      log('CSS Found', 'Найден <link rel="stylesheet"> — блокирует рендеринг!')
      log('CSS Parsing', 'Построение CSSOM...')
      log('Script Found', 'Найден <script defer> — скачивается параллельно')
      log('DOM Ready', 'DOMContentLoaded событие')
    
      // 3. Рендеринг
      log('Render Tree', 'DOM + CSSOM объединены')
      log('Layout', 'Вычисление позиций и размеров элементов')
      log('Paint', 'Отрисовка пикселей')
      log('Compositing', 'GPU объединяет слои')
    
      // 4. Финал
      log('Script Executed', 'defer-скрипты выполнены')
      log('Images Loaded', 'Все ресурсы загружены')
      log('load event', 'window.onload вызван')
    
      console.log('\n=== Итоговая последовательность ===')
      return steps
    }
    
    simulateBrowserLoading('example.com')
    
    // Демонстрация разницы async/defer/обычный
    console.log('\n=== Влияние скриптов на парсинг ===')
    
    const scenarios = [
      {
        type: 'Обычный <script>',
        timeline: [
          'Парсинг HTML....',
          '⏸️  ПАУЗА: скачиваем script.js',
          '⏸️  ПАУЗА: выполняем script.js',
          'Продолжаем парсинг HTML',
          'DOMContentLoaded'
        ]
      },
      {
        type: '<script async>',
        timeline: [
          'Парсинг HTML.... (параллельно скачиваем script.js)',
          '⏸️  ПАУЗА: выполняем script.js (как только скачался)',
          'Продолжаем парсинг HTML',
          'DOMContentLoaded'
        ]
      },
      {
        type: '<script defer>',
        timeline: [
          'Парсинг HTML.... (параллельно скачиваем script.js)',
          'DOMContentLoaded ← скрипт выполняется ЗДЕСЬ',
          'Выполняем script.js (после полного парсинга)',
        ]
      }
    ]
    
    for (const scenario of scenarios) {
      console.log(`\n${scenario.type}:`)
      scenario.timeline.forEach((step, i) => {
        console.log(`  ${i + 1}. ${step}`)
      })
    }

    Как браузер загружает страницу?

    Краткий ответ

    Загрузка страницы — это многоступенчатый процесс: сначала браузер устанавливает сетевое соединение (DNS + TCP), получает HTML и парсит его в DOM-дерево, параллельно строит CSSOM из стилей, затем объединяет их в Render Tree, вычисляет расположение элементов (Layout), рисует пиксели (Paint) и выводит результат на экран (Compositing). Понимание этого процесса критично для оптимизации производительности сайта.

    Полный разбор

    Этап 1: Сеть

    [Браузер] → DNS lookup → IP-адрес сервера
             → TCP Handshake (SYN → SYN-ACK → ACK)
             → TLS Handshake (для HTTPS)
             → HTTP GET /index.html
             ← HTTP 200 OK + тело HTML

    DNS lookup — перевод домена (example.com) в IP-адрес. Результат кешируется.

    TCP соединение — три рукопожатия перед передачей данных.

    TTFB (Time To First Byte) — время от запроса до первого байта ответа. Важная метрика.

    Этап 2: Парсинг HTML → DOM

    Браузер читает HTML сверху вниз и строит DOM-дерево (Document Object Model):

    HTML-текст → Tokenizer → Parser → DOM Tree
    
    <html>
      <head>...</head>
      <body>
        <div id="app">
          <p>Hello</p>
        </div>
      </body>
    </html>
    
          Document
             │
            html
           /    \
        head    body
                  │
                 div#app
                  │
                  p
                  │
               "Hello"

    Этап 3: Парсинг CSS → CSSOM

    Параллельно с DOM строится CSSOM (CSS Object Model). CSS — блокирующий ресурс для рендеринга: браузер не начнёт отрисовку, пока не загрузит и не распарсит все CSS-файлы в <head>.

    Этап 4: JavaScript — блокировка парсинга

    Синхронный <script> блокирует парсинг HTML:

    HTML парсится → встретили <script src="app.js"> →
    СТОП парсинга → скачать app.js → выполнить →
    ПРОДОЛЖИТЬ парсинг HTML

    Почему? Скрипт может вызвать document.write() и полностью изменить HTML.

    async и defer убирают блокировку:

    Обычный:  HTML ███░░░░░░███  (░ = пауза на скрипт)
    async:    HTML ████████████  (скрипт скачивается параллельно, выполняется сразу)
    defer:    HTML ████████████  (скрипт скачивается параллельно, выполняется после парсинга)

    | Атрибут | Скачивание | Выполнение | Порядок |

    |---------|-----------|-----------|---------|

    | (нет) | блокирует | сразу | да |

    | async | параллельно | как скачается | нет |

    | defer | параллельно | после парсинга HTML | да |

    Этап 5: Render Tree

    DOM + CSSOM объединяются в Render Tree — только видимые элементы с применёнными стилями. Элементы с display: none не попадают в Render Tree.

    Этап 6: Layout (Reflow)

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

    Этап 7: Paint

    Браузер рисует пиксели: цвета, тексты, изображения, тени. Каждый слой рисуется отдельно.

    Этап 8: Compositing

    GPU объединяет слои в финальное изображение на экране.

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

    // DOMContentLoaded — HTML распарсен, DOM готов
    // Скрипты с defer выполнились. Картинки могут ещё грузиться.
    document.addEventListener('DOMContentLoaded', () => {
      console.log('DOM готов — можно работать с элементами')
    })
    
    // load — ВСЁ загружено: картинки, стили, скрипты
    window.addEventListener('load', () => {
      console.log('Страница полностью загружена')
    })

    Правило: используй DOMContentLoaded для инициализации UI. Используй load только когда нужны размеры картинок.

    Критический путь рендеринга (Critical Rendering Path)

    Минимальная цепочка для первой отрисовки страницы:

    HTML → DOM → Render Tree → Layout → Paint
           CSS → CSSOM ↗

    Оптимизация CRP:

  • CSS — в <head> (чтобы не блокировать рендеринг после парсинга)
  • JS — в конце <body> или с defer
  • Минимизировать блокирующие ресурсы
  • Использовать <link rel="preload"> для критических ресурсов
  • Связанные уроки курса

  • DOM — работа с деревом документа
  • Жизненный цикл страницы (DOMContentLoaded, load, beforeunload)
  • async и defer скрипты
  • Как отвечать на собеседовании

    Начни с общей картины: "Загрузка страницы проходит в несколько этапов: сеть, парсинг, рендеринг". Это показывает системное мышление.

    Обязательно упомяни: блокирующий характер синхронных скриптов и CSS — это ключевой момент для оптимизации. Разницу между DOMContentLoaded и load события.

    Хорошее дополнение: упомяни Critical Rendering Path и что ты делал бы для оптимизации (defer, preload, CSS в head).

    Время ответа: 2-3 минуты. Можно нарисовать схему на доске.

    Красные флаги ответа

    1. "Браузер просто открывает страницу" — полное непонимание процесса. Это показывает, что кандидат никогда не занимался оптимизацией.

    2. Путаница DOMContentLoaded и load — очень частая ошибка, из-за которой возникают баги с "элемент не найден".

    3. Незнание про блокирующие скрипты — если не знаешь, почему <script> в <head> без defer замедляет загрузку, это серьёзный пробел.

    Примеры

    Симуляция последовательности загрузки браузера с временными метками

    // Симуляция этапов загрузки браузера
    // В реальности эти этапы выполняет движок браузера
    
    function simulateBrowserLoading(url) {
      const steps = []
      const startTime = Date.now()
    
      function log(step, detail = '') {
        const elapsed = Date.now() - startTime
        steps.push({ step, detail, ms: elapsed })
        console.log(`[${String(elapsed).padStart(4)}ms] ${step}${detail ? ': ' + detail : ''}`)
      }
    
      console.log(`=== Загрузка ${url} ===\n`)
    
      // 1. Сеть
      log('DNS lookup', 'example.com → 93.184.216.34')
      log('TCP Handshake', 'SYN → SYN-ACK → ACK')
      log('TLS Handshake', 'HTTPS шифрование')
      log('HTTP GET', `GET / HTTP/1.1 Host: ${url}`)
      log('HTTP 200 OK', 'Получен HTML (12 KB)')
    
      // 2. Парсинг
      log('HTML Parsing', 'Построение DOM-дерева...')
      log('CSS Found', 'Найден <link rel="stylesheet"> — блокирует рендеринг!')
      log('CSS Parsing', 'Построение CSSOM...')
      log('Script Found', 'Найден <script defer> — скачивается параллельно')
      log('DOM Ready', 'DOMContentLoaded событие')
    
      // 3. Рендеринг
      log('Render Tree', 'DOM + CSSOM объединены')
      log('Layout', 'Вычисление позиций и размеров элементов')
      log('Paint', 'Отрисовка пикселей')
      log('Compositing', 'GPU объединяет слои')
    
      // 4. Финал
      log('Script Executed', 'defer-скрипты выполнены')
      log('Images Loaded', 'Все ресурсы загружены')
      log('load event', 'window.onload вызван')
    
      console.log('\n=== Итоговая последовательность ===')
      return steps
    }
    
    simulateBrowserLoading('example.com')
    
    // Демонстрация разницы async/defer/обычный
    console.log('\n=== Влияние скриптов на парсинг ===')
    
    const scenarios = [
      {
        type: 'Обычный <script>',
        timeline: [
          'Парсинг HTML....',
          '⏸️  ПАУЗА: скачиваем script.js',
          '⏸️  ПАУЗА: выполняем script.js',
          'Продолжаем парсинг HTML',
          'DOMContentLoaded'
        ]
      },
      {
        type: '<script async>',
        timeline: [
          'Парсинг HTML.... (параллельно скачиваем script.js)',
          '⏸️  ПАУЗА: выполняем script.js (как только скачался)',
          'Продолжаем парсинг HTML',
          'DOMContentLoaded'
        ]
      },
      {
        type: '<script defer>',
        timeline: [
          'Парсинг HTML.... (параллельно скачиваем script.js)',
          'DOMContentLoaded ← скрипт выполняется ЗДЕСЬ',
          'Выполняем script.js (после полного парсинга)',
        ]
      }
    ]
    
    for (const scenario of scenarios) {
      console.log(`\n${scenario.type}:`)
      scenario.timeline.forEach((step, i) => {
        console.log(`  ${i + 1}. ${step}`)
      })
    }

    Задание

    Расставь этапы загрузки браузера в правильном порядке. Реализуй функцию sortLoadingSteps(steps), которая принимает массив этапов в случайном порядке и возвращает их в правильной последовательности. Этапы: "DNS lookup", "TCP Handshake", "HTTP Request", "HTML Parsing", "CSS Parsing", "Render Tree", "Layout", "Paint", "Compositing". Используй объект LOADING_STAGES как справочник порядка.

    Подсказка

    Для сортировки используй LOADING_STAGES[a] - LOADING_STAGES[b]. После HTML Parsing стреляет DOMContentLoaded. HTML Parsing блокирует синхронный <script>.

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