\n \n \n \n

Hello

\n \n \n \n\n```\n\n### Почему это происходит\n\nJavaScript может получить доступ к DOM через `document.write()`, `document.getElementById()` и т.д. Поэтому браузер не может продолжить парсинг, пока скрипт не выполнится.\n\n```javascript\n// JavaScript может изменять DOM\ndocument.write('Injected content');\ndocument.body.innerHTML = '
New content
';\n\n// Поэтому браузер блокирует парсинг\n```\n\n### Визуальное представление\n\n```\nDOM Parsing Timeline:\n\nБез script:\n[=== HTML parse ===][== CSS parse ==][=== Rendering ===]\nВсё быстро\n\nС script в :\n[HTML parse] STOP\n [Download + Execute script]\n [Continue HTML parse]\n [CSS parse]\n [Rendering]\nМедленнее\n\nС async script:\n[HTML parse ===============][CSS parse][Rendering]\n [Download + Execute script in parallel]\nБыстрее\n```\n\n### Решения: Как избежать блокирования\n\n#### 1. Переместить script в конец body\n\n```html\n\n \n My App\n \n \n \n

Hello

\n

Content

\n\n \n \n \n\n\n\n```\n\n#### 2. Использовать async атрибут\n\n```html\n\n\n\n```\n\n#### 3. Использовать defer атрибут (РЕКОМЕНДУЕТСЯ)\n\n```html\n\n\n\n```\n\n#### Сравнение async vs defer\n\n```html\n \n \n \n```\n\nTimeline:\n\n```\nHTML parsing: [====================================]\nScript a: [X] STOP PARSING\nScript b: [download]-> [execute]\nScript c: [download] -> [execute after DOM]\n```\n\n### Практический пример в Next.js / React\n\n```typescript\n// pages/_document.tsx (Next.js)\n\nexport default function Document() {\n return (\n \n \n {/* Для скриптов, которые нужны в head (редко) */}\n \n \n \n
\n \n\n {/* Для скриптов в конце body */}\n \n \n \n );\n}\n```\n\n### Пример: Блокирование vs неблокирование\n\n#### Плохо: Скрипт блокирует парсинг\n\n```html\n\n \n \n \n \n \n
Welcome!
\n
...
\n \n\n\n\n```\n\n#### Хорошо: Скрипт не блокирует\n\n```html\n\n \n My App\n \n \n
Welcome!
\n
...
\n\n \n \n\n\n\n```\n\n**Результат: FCP улучшился с 5s до 1s!**\n\n### Специальные случаи\n\n#### 1. Inline скрипты тоже блокируют\n\n```html\n\n\n\n```\n\n#### 2. CSS тоже может блокировать (косвенно)\n\n```html\n\n\n\n\n\n\n```\n\n#### 3. Module скрипты (ES6)\n\n```html\n\n\n\n\n\n```\n\n### Практические рекомендации\n\n#### Чек-лист оптимизации скриптов\n\n- [ ] **Основные скрипты в конце ``** — парсинг не блокируется\n- [ ] **Используй `defer` для скриптов в ``** — если очень нужны там\n- [ ] **Используй `async` для независимых скриптов** — аналитика, трекинг\n- [ ] **Минимизируй размер скриптов** — разделяй код на чанки\n- [ ] **Lazy loading для некритичного кода** — динамический import()\n- [ ] **Проверяй Core Web Vitals** — LCP, FID, CLS\n\n#### Пример оптимизации\n\n```html\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n\n \n \n \n \n \n \n\n \n \n \n \n\n```\n\n### Web Vitals и Script Parsing\n\nСкрипты напрямую влияют на:\n- **FCP (First Contentful Paint)** — блокирующие скрипты замедляют\n- **LCP (Largest Contentful Paint)** — скрипты могут отложить загрузку изображений\n- **FID (First Input Delay)** — выполнение скриптов блокирует обработку пользовательского ввода\n\n### Выводы\n\n- **`
← Назад к вопросам

Блокирует ли тег script формирование DOM-дерева?

2.0 Middle🔥 231 комментариев
#HTML и CSS#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Тег script и блокирование парсинга DOM

Да, <script> тег блокирует формирование DOM-дерева. Это один из самых важных аспектов оптимизации производительности веб-страниц. Понимание этого критично для frontend разработчика.

Как работает парсинг HTML и выполнение JavaScript

Базовый процесс

  1. Парсер HTML читает HTML файл сверху вниз
  2. Построить DOM дерево из HTML элементов
  3. Когда парсер встречает <script> тег:
    • Парсер ОСТАНАВЛИВАЕТСЯ
    • Скачивается и выполняется JavaScript
    • Потом парсинг продолжается дальше
<html>
  <head>
    <script src="large-library.js"></script>
    <!-- Парсинг БЛОКИРУЕТСЯ здесь -->
  </head>
  <body>
    <h1>Hello</h1>
    <!-- Парсер дождётся выполнения скрипта перед парсингом h1 -->
    <script>
      // Inline script тоже блокирует
      console.log('After h1');
    </script>
  </body>
</html>

Почему это происходит

JavaScript может получить доступ к DOM через document.write(), document.getElementById() и т.д. Поэтому браузер не может продолжить парсинг, пока скрипт не выполнится.

// JavaScript может изменять DOM
document.write('Injected content');
document.body.innerHTML = '<div>New content</div>';

// Поэтому браузер блокирует парсинг

Визуальное представление

DOM Parsing Timeline:

Без script:
[=== HTML parse ===][== CSS parse ==][=== Rendering ===]
Всё быстро

С script в <head>:
[HTML parse]  STOP
              [Download + Execute script]
              [Continue HTML parse]
              [CSS parse]
              [Rendering]
Медленнее

С async script:
[HTML parse ===============][CSS parse][Rendering]
          [Download + Execute script in parallel]
Быстрее

Решения: Как избежать блокирования

1. Переместить script в конец body

<html>
  <head>
    <title>My App</title>
    <!-- Без script здесь -->
  </head>
  <body>
    <h1>Hello</h1>
    <p>Content</p>

    <!-- Script в конце -->
    <script src="app.js"></script>
  </body>
</html>

<!-- Преимущества:
  - DOM построен перед выполнением скрипта
  - Пользователь видит контент раньше
  - Скрипт может сразу работать с DOM
-->

2. Использовать async атрибут

<script async src="analytics.js"></script>

<!-- 
  async = скрипт загружается асинхронно, не блокирует парсинг
  Недостатки:
  - Может выполниться ДО парсинга всего DOM
  - Порядок выполнения непредсказуем, если несколько скриптов
  
  Используй для:
  - Аналитики (Google Analytics)
  - Трекинга (Sentry)
  - Скриптов, независимых от DOM
-->

3. Использовать defer атрибут (РЕКОМЕНДУЕТСЯ)

<script defer src="main.js"></script>

<!--
  defer = скрипт загружается асинхронно, но выполняется ПОСЛЕ парсинга DOM
  Это лучший выбор для большинства скриптов!
  
  Порядок выполнения гарантирован (если несколько скриптов с defer)
  DOM полностью готов перед выполнением
-->

Сравнение async vs defer

<script src="a.js"></script>          <!-- Блокирует -->
<script async src="b.js"></script>   <!-- Загружается параллельно, выполняется АСАП -->
<script defer src="c.js"></script>   <!-- Загружается параллельно, выполняется после DOM -->

Timeline:

HTML parsing:    [====================================]
Script a:        [X] STOP PARSING
Script b:                    [download]-> [execute]
Script c:                    [download]                -> [execute after DOM]

Практический пример в Next.js / React

// pages/_document.tsx (Next.js)

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        {/* Для скриптов, которые нужны в head (редко) */}
        <script
          async
          src="https://www.googletagmanager.com/gtag/js?id=GA_ID"
        />
      </Head>
      <body>
        <Main />
        <NextScript />

        {/* Для скриптов в конце body */}
        <script
          defer
          src="https://example.com/tracking.js"
        />
      </body>
    </Html>
  );
}

Пример: Блокирование vs неблокирование

Плохо: Скрипт блокирует парсинг

<html>
  <head>
    <script src="https://cdn.example.com/huge-library.js"></script>
    <!-- 3 секунды загрузки = пользователь не видит контент 3 секунды -->
  </head>
  <body>
    <div class="hero">Welcome!</div>
    <div class="content">...</div>
  </body>
</html>

<!-- Timeline:
0s:   [Загрузка huge-library.js ...]
3s:   [Выполнение huge-library.js]
4s:   [Парсинг HTML]
5s:   [FCP - First Contentful Paint]
-->

Хорошо: Скрипт не блокирует

<html>
  <head>
    <title>My App</title>
  </head>
  <body>
    <div class="hero">Welcome!</div>
    <div class="content">...</div>

    <script defer src="https://cdn.example.com/huge-library.js"></script>
  </body>
</html>

<!-- Timeline:
0s:   [Парсинг HTML]
1s:   [FCP - First Contentful Paint, пользователь видит контент!]
1s:   [Загрузка huge-library.js в фоне]
4s:   [Выполнение huge-library.js после DOM]
-->

Результат: FCP улучшился с 5s до 1s!

Специальные случаи

1. Inline скрипты тоже блокируют

<script>
  // Этот inline скрипт тоже блокирует парсинг
  const data = fetch('/api/data').then(r => r.json());
  console.log(data);
</script>

<!-- Парсинг ждёт выполнения этого скрипта -->

2. CSS тоже может блокировать (косвенно)

<link rel="stylesheet" href="styles.css" />
<!-- CSS не блокирует парсинг HTML, но -->

<script src="app.js"></script>
<!-- Этот скрипт ЖДЁТ загрузки CSS перед выполнением -->
<!-- Если CSS медленный, скрипт тоже медленный -->

3. Module скрипты (ES6)

<script type="module" src="app.js"></script>
<!-- Module скрипты ведут себя как defer по умолчанию -->
<!-- Загружаются асинхронно, выполняются после DOM -->

<script type="module">
  // Inline module скрипт тоже асинхронный
  import { App } from './app.js';
</script>

Практические рекомендации

Чек-лист оптимизации скриптов

  • Основные скрипты в конце <body> — парсинг не блокируется
  • Используй defer для скриптов в <head> — если очень нужны там
  • Используй async для независимых скриптов — аналитика, трекинг
  • Минимизируй размер скриптов — разделяй код на чанки
  • Lazy loading для некритичного кода — динамический import()
  • Проверяй Core Web Vitals — LCP, FID, CLS

Пример оптимизации

<!-- Плохо -->
<html>
  <head>
    <script src="utils.js"></script>
    <script src="analytics.js"></script>
    <script src="tracking.js"></script>
    <script src="app.js"></script>
  </head>
  <body>
    <app />
  </body>
</html>

<!-- Хорошо -->
<html>
  <head>
    <!-- Только критичные скрипты, async для независимых -->
    <script async src="analytics.js"></script>
  </head>
  <body>
    <app />

    <!-- Основной код в конце с defer -->
    <script defer src="app.js"></script>
    <!-- app.js импортирует utils.js динамически -->
  </body>
</html>

Web Vitals и Script Parsing

Скрипты напрямую влияют на:

  • FCP (First Contentful Paint) — блокирующие скрипты замедляют
  • LCP (Largest Contentful Paint) — скрипты могут отложить загрузку изображений
  • FID (First Input Delay) — выполнение скриптов блокирует обработку пользовательского ввода

Выводы

  • <script> БЕЗ атрибутов = блокирует парсинг DOM
  • <script defer> = загружается асинхронно, выполняется после DOM (РЕКОМЕНДУЕТСЯ)
  • <script async> = загружается асинхронно, выполняется АСАП
  • В конце <body> = естественно избегает блокирования
  • Module скрипты = асинхронные по умолчанию

Правильное размещение и атрибуты скриптов — это низко висящий фрукт для оптимизации производительности!