Подключение скриптов: script тег

Тег <script> подключает JavaScript к HTML-странице: либо встроенный код, либо внешний файл; атрибуты defer и async управляют порядком загрузки и выполнения.

Зачем нужно

Неправильное подключение скриптов ведёт к блокировке рендеринга страницы, ошибкам «элемент не найден» и медленной загрузке. Понимание defer/async и типа модуля позволяет оптимально управлять порядком выполнения и производительностью.

Где используется

  • Подключение JavaScript в любом HTML-документе
  • Подключение библиотек (jQuery, analytics, tracking)
  • Модульные приложения (type="module")
  • Динамическая загрузка скриптов в runtime

Базовый синтаксис

<!-- Встроенный код (inline script) -->
<script>
  console.log('Привет, мир!');
</script>

<!-- Внешний файл -->
<script src="./app.js"></script>

<!-- ES-модуль -->
<script type="module" src="./main.js"></script>

Обычный script (блокирующий)

Браузер останавливает парсинг HTML, загружает и выполняет скрипт, затем продолжает.

<head>
  <!-- Плохо в <head> без defer/async: блокирует рендер -->
  <script src="heavy.js"></script>
</head>

Исторический приём — ставить <script> перед </body>:

<body>
  <div id="app"></div>
  <!-- DOM уже построен к моменту выполнения -->
  <script src="app.js"></script>
</body>

defer — отложенное выполнение

Скрипт загружается параллельно с HTML, выполняется после полного парсинга DOM, в порядке объявления:

<head>
  <!-- Загружаются параллельно, выполняются по порядку после DOM -->
  <script defer src="lib.js"></script>
  <script defer src="app.js"></script>
  <!-- app.js всегда запустится после lib.js -->
</head>

Эквивалентно обработчику DOMContentLoaded.

async — асинхронное выполнение

Скрипт загружается параллельно и выполняется сразу после загрузки, не ожидая DOM и других скриптов:

<head>
  <!-- Выполнится как только загрузится — порядок не гарантирован -->
  <script async src="analytics.js"></script>
  <script async src="ads.js"></script>
</head>

Подходит для независимых скриптов (аналитика, реклама).

Сравнительная таблица

Атрибут Загрузка Блокировка HTML Порядок
(нет) Синхронная Да Да
defer Параллельная Нет Сохраняется
async Параллельная Нет Не гарантирован

type="module"

<script type="module" src="./main.js"></script>
  • Автоматически defer по умолчанию
  • Строгий режим ('use strict')
  • Изолированная область видимости
  • Поддержка import/export
  • Загружается один раз, даже при нескольких тегах с одним src

Динамическое подключение скрипта

function loadScript(src) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = src;
    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error(`Ошибка загрузки: ${src}`));
    document.head.appendChild(script);
  });
}

// Ленивая загрузка
button.addEventListener('click', async () => {
  await loadScript('/libs/chart.js');
  renderChart;
});

Частые ошибки

1. defer с inline-скриптами не работает

<!-- defer игнорируется для встроенного кода -->
<script defer>
  document.querySelector('#app'); // может быть null — defer не применяется
</script>

2. type="module" блокирует доступ к глобальным переменным

<script type="module">
  window.myVar = 42; // OK, но...
</script>
<script>
  console.log(myVar); // может не найти, если другой скрипт выполнился раньше
</script>

3. Нарушение порядка зависимостей с async

<!-- app.js зависит от lib.js, но async не гарантирует порядок -->
<script async src="lib.js"></script>
<script async src="app.js"></script> <!-- может запуститься до lib.js! -->
<!-- Используй defer или bundler -->

Связанные темы

Ресурсы