Веб‑проект почти всегда нуждается в JavaScript‑когда‑то‑бы. Но не каждый разработчик задумывается, где именно в документе разместить скрипты. Неправильное расположение может добавить секунды к времени первой отрисовки и оттолкнуть посетителей. В этой статье разберём, какие варианты существуют, какие плюсы и минусы у каждого, и какие атрибуты помогают ускорить загрузку.
В веб‑проекте правильное размещение HTML‑элемент <script> это часть HTML‑документа, позволяющая выполнять JavaScript‑код в браузере напрямую влияет на скорость загрузки и отзывчивость страницы.
Браузер читает HTML последовательно. Когда он встречает тег <script>, он останавливает построение DOM, загружает указанный файл (если он внешний) и исполняет код. Пока это не завершится, дальнейшее отображение страницы приостановлено. Поэтому размещение скриптов в стратегически правильных местах позволяет избежать «блокировки рендеринга».
Традиционно скрипты помещали в раздел HTML язык разметки для создания веб‑страниц - в <head>. Это гарантировало, что код будет готов к использованию сразу после загрузки страницы. Но такой подход часто задерживает первую отрисовку, особенно если скриптов несколько крупных.
Современный подход - добавлять их перед закрывающим тегом <body> основная часть HTML‑документа, где размещается контент. В этом случае браузер сначала построит DOM и покажет пользователю контент, а уже потом загрузит и выполнит скрипты.
Итого, простое правило: помещайте тяжёлые скрипты в конец <body>, а небольшие, критические - в <head> с атрибутами, описанными ниже.
HTML5 ввёл два полезных атрибута, позволяющих загружать скрипты асинхронно, не блокируя рендеринг.
async ускорит процесс.Ключевое различие: async может выполнить код до полной загрузки DOM, а defer гарантирует, что весь HTML уже будет построен.
Инлайн‑скрипты (код прямо внутри тега <script>) экономят один HTTP‑запрос, но увеличивают размер HTML‑файла и усложняют кэширование. Внешние файлы позволяют использовать кэш браузера, разделять код по модулям и подключать инструменты сборки.
Если скрипт важен для первой отрисовки (например, небольшой «плейсхолдер»), его удобно разместить инлайн в <head>. Для всех остальных - подключайте внешний файл, применяя defer или async в зависимости от зависимости.
С появлением module тип скрипта, поддерживающий импорт/экспорт ES6‑модулей можно объявлять <script type="module">. Браузер автоматически ставит defer‑поведение, а также позволяет использовать import‑запросы.
Если ваш сайт использует Content Security Policy политика безопасности, ограничивающая загрузку ресурсов, инлайн‑скрипты могут быть заблокированы. В таком случае рекомендуется вынести весь код во внешние файлы.
<head> с defer или async.<body> и добавьте атрибут defer.unsafe-inline или хеш‑значения.
| Атрибут | Загрузка | Выполнение | Подходит для |
|---|---|---|---|
async |
Параллельно с парсингом HTML | Сразу после полной загрузки файла | Самодостаточные скрипты, аналитика, рекламу |
defer |
Параллельно с парсингом HTML | После завершения парсинга всего HTML | Скрипты, работающие с DOM, UI‑логика |
<head> без defer - приводит к длительному времени первого отклика. Решение: перенести в конец <body> или добавить defer.type="module" при использовании ES6‑импортов - скрипт не выполнится. Решение: добавить type="module" и убедиться, что сервер отдаёт правильный MIME‑тип.Если вам нужен быстрый старт, следуйте простому правилу: крупные библиотеки в конец <body> с defer, аналитика - async в <head>, а критический UI‑код - либо defer, либо инлайн с надёжным CSP.
<script> из <head>?Да, если все ваши скрипты находятся в конце <body> с атрибутом defer или async. Это не нарушит работу, но убедитесь, что нет кода, который нужен до построения DOM.
async?Тогда, когда скрипт полностью независим от DOM и от остальных скриптов, например, счетчики, рекламные пиксели или сторонние аналитические библиотеки.
defer?Да. При defer скрипты сохраняют порядок, в котором они указаны в HTML. Поэтому зависимости следует размещать выше, чем скрипты, которые их используют.
async и defer в одном теге?Нет. Если указать оба атрибута, браузер будет действовать как будто присутствует только async. Выберите тот, который лучше подходит для вашего сценария.
Либо добавить хеш‑значение конкретного инлайн‑кода в политику, либо полностью вынести код во внешний файл и указать нужные источники в директиве script-src.