Веб‑проект почти всегда нуждается в JavaScript‑когда‑то‑бы. Но не каждый разработчик задумывается, где именно в документе разместить скрипты. Неправильное расположение может добавить секунды к времени первой отрисовки и оттолкнуть посетителей. В этой статье разберём, какие варианты существуют, какие плюсы и минусы у каждого, и какие атрибуты помогают ускорить загрузку.
В веб‑проекте правильное размещение HTML‑элемент <script> это часть HTML‑документа, позволяющая выполнять JavaScript‑код в браузере напрямую влияет на скорость загрузки и отзывчивость страницы.
Почему место размещения скрипта имеет значение
Браузер читает HTML последовательно. Когда он встречает тег <script>, он останавливает построение DOM, загружает указанный файл (если он внешний) и исполняет код. Пока это не завершится, дальнейшее отображение страницы приостановлено. Поэтому размещение скриптов в стратегически правильных местах позволяет избежать «блокировки рендеринга».
Основные варианты: или
Традиционно скрипты помещали в раздел HTML язык разметки для создания веб‑страниц - в <head>. Это гарантировало, что код будет готов к использованию сразу после загрузки страницы. Но такой подход часто задерживает первую отрисовку, особенно если скриптов несколько крупных.
Современный подход - добавлять их перед закрывающим тегом <body> основная часть HTML‑документа, где размещается контент. В этом случае браузер сначала построит DOM и покажет пользователю контент, а уже потом загрузит и выполнит скрипты.
Итого, простое правило: помещайте тяжёлые скрипты в конец <body>, а небольшие, критические - в <head> с атрибутами, описанными ниже.
Атрибуты async и defer: как они работают
HTML5 ввёл два полезных атрибута, позволяющих загружать скрипты асинхронно, не блокируя рендеринг.
- async загружает скрипт параллельно с построением DOM и исполняет его сразу после загрузки. Если скрипт не зависит от других,
asyncускорит процесс. - defer загружает скрипт параллельно, но откладывает его исполнение до конца парсинга HTML. Это безопасный вариант для скриптов, которые взаимодействуют с DOM.
Ключевое различие: async может выполнить код до полной загрузки DOM, а defer гарантирует, что весь HTML уже будет построен.
Инлайн‑скрипты vs внешние файлы
Инлайн‑скрипты (код прямо внутри тега <script>) экономят один HTTP‑запрос, но увеличивают размер HTML‑файла и усложняют кэширование. Внешние файлы позволяют использовать кэш браузера, разделять код по модулям и подключать инструменты сборки.
Если скрипт важен для первой отрисовки (например, небольшой «плейсхолдер»), его удобно разместить инлайн в <head>. Для всех остальных - подключайте внешний файл, применяя defer или async в зависимости от зависимости.
Продвинутые техники: модули и CSP
С появлением module тип скрипта, поддерживающий импорт/экспорт ES6‑модулей можно объявлять <script type="module">. Браузер автоматически ставит defer‑поведение, а также позволяет использовать import‑запросы.
Если ваш сайт использует Content Security Policy политика безопасности, ограничивающая загрузку ресурсов, инлайн‑скрипты могут быть заблокированы. В таком случае рекомендуется вынести весь код во внешние файлы.
Практический чеклист размещения скриптов
- Определите, нужен ли скрипт до полной отрисовки. Если да - разместите его в
<head>сdeferилиasync. - Для большинства UI‑логики переместите файл в конец
<body>и добавьте атрибутdefer. - Если скрипт небольшого размера (< 2 KB) и критичен, используйте инлайн‑вариант, но только после настройки CSP, позволяющей
unsafe-inlineили хеш‑значения. - Для больших библиотек (React, Vue, jQuery) подключайте их как внешние файлы, включайте кэширование и сжимайте (gzip/ brotli).
- Проверяйте порядок загрузки в инструментах разработчика: сеть → время блока рендеринга.
- Тестируйте на медленных соединениях (3G, 2G) с помощью Lighthouse, обращая внимание на метрику «Time to Interactive».
Таблица сравнения атрибутов async и defer
| Атрибут | Загрузка | Выполнение | Подходит для |
|---|---|---|---|
async |
Параллельно с парсингом HTML | Сразу после полной загрузки файла | Самодостаточные скрипты, аналитика, рекламу |
defer |
Параллельно с парсингом HTML | После завершения парсинга всего HTML | Скрипты, работающие с DOM, UI‑логика |
Распространённые ошибки и их решение
- Размещение тяжёлых библиотек в
<head>безdefer- приводит к длительному времени первого отклика. Решение: перенести в конец<body>или добавитьdefer. - Использование нескольких инлайн‑скриптов с конфликтующими переменными. Решение: применять модули или IIFE‑обёртки.
- Забытый атрибут
type="module"при использовании ES6‑импортов - скрипт не выполнится. Решение: добавитьtype="module"и убедиться, что сервер отдаёт правильный MIME‑тип. - Неучтённые зависимости: скрипт A использует B, но B загружается позже. Решение: порядок подключения, либо использование bundler‑ов (Webpack инструмент сборки модулей JavaScript или Babel транспайлер, преобразующий современный JS в совместимый код).
Краткое резюме для практикующих разработчиков
Если вам нужен быстрый старт, следуйте простому правилу: крупные библиотеки в конец <body> с defer, аналитика - async в <head>, а критический UI‑код - либо defer, либо инлайн с надёжным CSP.
Можно ли полностью убрать тег <script> из <head>?
Да, если все ваши скрипты находятся в конце <body> с атрибутом defer или async. Это не нарушит работу, но убедитесь, что нет кода, который нужен до построения DOM.
Когда стоит использовать атрибут async?
Тогда, когда скрипт полностью независим от DOM и от остальных скриптов, например, счетчики, рекламные пиксели или сторонние аналитические библиотеки.
Влияет ли порядок подключения внешних скриптов на defer?
Да. При defer скрипты сохраняют порядок, в котором они указаны в HTML. Поэтому зависимости следует размещать выше, чем скрипты, которые их используют.
Можно ли комбинировать async и defer в одном теге?
Нет. Если указать оба атрибута, браузер будет действовать как будто присутствует только async. Выберите тот, который лучше подходит для вашего сценария.
Что делать, если CSP запрещает инлайн‑скрипты?
Либо добавить хеш‑значение конкретного инлайн‑кода в политику, либо полностью вынести код во внешний файл и указать нужные источники в директиве script-src.