Где размещать теги <script> в HTML-разметке?
При встраивании JavaScript в HTML-документ, в каком месте следует размещать теги <script> и включаемый JavaScript? Я помню, что не рекомендуется размещать их в секции <head>, но размещение в начале секции <body> тоже плохо, поскольку JavaScript должен быть обработан до полной отрисовки страницы. Это оставляет конец секции <body> в качестве логичного места для тегов <script>.
Так где же правильно размещать теги <script>?
(Примечание: Этот вопрос ссылается на связанное обсуждение о переносе вызовов JavaScript-функций из тегов <a> в теги <script>. Я использую jQuery, но также приветствуются общие ответы.)
Размещение тегов <script> в HTML зависит от нескольких факторов, включая производительность, функциональные требования и современные практики веб-разработки. Хотя традиционно размещение скриптов в конце <body> рекомендовалось для предотвращения блокировки отрисовки страницы, современные подходы предлагают большую гибкость и возможности оптимизации производительности.
Содержание
- Традиционные подходы к размещению скриптов
- Современные стратегии загрузки скриптов
- Соображения по производительности
- Рекомендации, специфичные для jQuery
- Лучшие практики в зависимости от случая использования
- Продвинутые техники оптимизации
Традиционные подходы к размещению скриптов
Размещение в конце секции <body>
Традиционный подход размещает теги <script> непосредственно перед закрывающим тегом </body>. Этот метод предотвращает блокировку начальной отрисовки страницы JavaScript, поскольку браузер сначала отрисовывает весь HTML-контент, а затем выполняет скрипты.
<!DOCTYPE html>
<html>
<head>
<title>Моя страница</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Добро пожаловать на мою страницу</h1>
<p>Этот контент загружается быстро.</p>
<!-- Скрипты в конце body -->
<script src="jquery.js"></script>
<script src="app.js"></script>
</body>
</html>
Преимущества:
- Быстрая отрисовка контента
- Отсутствие поведения, блокирующего отрисовку
- DOM-элементы доступны при выполнении скриптов
Размещение в начале секции <body>
Размещение скриптов в начале секции <body> обычно не рекомендуется, так как это блокирует отрисовку. Браузер должен загрузить и разобрать JavaScript, прежде чем продолжить отрисовку остального контента страницы.
<body>
<!-- Плохая практика - блокирует отрисовку -->
<script src="jquery.js"></script>
<script src="app.js"></script>
<h1>Добро пожаловать на мою страницу</h1>
<p>Этот контент не появится, пока не загрузятся скрипты.</p>
</body>
В секции <head>
Хотя размещение скриптов в секции <head> блокирует отрисовку, существуют случаи, когда это необходимо, особенно для библиотек, которые должны загружаться до любого манипулирования DOM.
<head>
<title>Моя страница</title>
<script src="jquery.js"></script> <!-- Может быть необходимо здесь -->
</head>
Современные стратегии загрузки скриптов
Атрибуты Async и Defer
Современный HTML предоставляет атрибуты async и defer для управления поведением загрузки скриптов без перемещения их в конец документа.
Атрибут Async:
<script src="analytics.js" async></script>
- Загружается без блокировки разбора
- Выполняется сразу после завершения загрузки
- Не гарантируется выполнение в порядке
- Лучше для независимых скриптов, таких как аналитика
Атрибут Defer:
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
- Загружается без блокировки разбора
- Выполняется после завершения разбора документа
- Сохраняет порядок выполнения
- Лучше для зависимых скриптов
Модульные скрипты
Модули ES6 могут использовать атрибут type="module", который автоматически применяет отложенную загрузку:
<script type="module" src="main.js"></script>
Соображения по производительности
Поведение, блокирующее отрисовку
- Без async/defer: Скрипты блокируют отрисовку независимо от размещения
- С async/defer: Скрипты загружаются асинхронно, но могут все равно блокировать отрисовку в определенных условиях
- Критический путь отрисовки: Понимание процесса отрисовки браузера помогает оптимизировать размещение скриптов
Влияние загрузки на Largest Contentful Paint (LCP)
- Скрипты, блокирующие отрисовку, могут значительно повлиять на оценки LCP
- Некритические скрипты должны загружаться после элементов LCP
- Рассмотрите возможность использования
loading="lazy"для необязательных скриптов
<script src="non-critical-feature.js" loading="lazy"></script>
Рекомендации, специфичные для jQuery
Загрузка jQuery перед зависимыми скриптами
При использовании jQuery убедитесь, что он загружается перед любыми зависимыми скриптами:
<!-- Вариант 1: С defer -->
<script src="jquery.js" defer></script>
<script src="my-jquery-code.js" defer></script>
<!-- Вариант 2: В конце body -->
<body>
<!-- Контент страницы -->
<script src="jquery.js"></script>
<script src="my-jquery-code.js"></script>
</body>
Размещение jQuery CDN
jQuery часто загружается из CDN. Современные CDN поддерживают функции, такие как preconnect и DNS-prefetch:
<head>
<link rel="preconnect" href="https://code.jquery.com">
<link rel="dns-prefetch" href="https://code.jquery.com">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
</head>
Лучшие практики в зависимости от случая использования
Критические скрипты
- Используйте встроенный в
<head>сasyncдля критической функциональности - Рассмотрите возможность размещения критического JavaScript в
<head>сdefer
<head>
<script>
// Критический встроенный скрипт
function criticalFunction() {
// Необходимая функциональность
}
</script>
<script src="critical-library.js" defer></script>
</head>
Сторонние скрипты
- Используйте
asyncдля независимых сторонних скриптов (аналитика, реклама) - Используйте
deferдля скриптов, зависящих от DOM или других скриптов
Приложенческие скрипты
- Используйте
deferдля большинства приложенческих скриптов JavaScript - Размещайте в конце
<body>, если не используются атрибуты async/defer
Продвинутые техники оптимизации
Подсказки ресурсов
Реализуйте подсказки ресурсов для оптимизации загрузки скриптов:
<head>
<!-- Preconnect к CDN -->
<link rel="preconnect" href="https://cdn.example.com">
<!-- DNS prefetch для домена -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- Preload критических скриптов -->
<link rel="preload" href="critical.js" as="script">
</head>
Динамическая загрузка скриптов
Загружайте скрипты динамически при необходимости:
function loadScript(src, callback) {
const script = document.createElement('script');
script.src = src;
script.onload = callback;
document.body.appendChild(script);
}
// Загрузка некритического скрипта после взаимодействия со страницей
document.addEventListener('click', () => {
loadScript('interactive-feature.js', () => {
// Инициализация функции
});
});
Бандлинг скриптов и разделение кода
- Объединяйте связанные скрипты вместе
- Реализуйте разделение кода для больших приложений
- Используйте современные сборщики, такие как Webpack, Rollup или Vite
Мониторинг производительности
Мониторьте производительность загрузки скриптов:
<script>
// Мониторинг производительности
window.addEventListener('load', () => {
const loadTime = performance.now();
console.log('Страница загружена за:', loadTime, 'мс');
});
</script>
Заключение
Оптимальное размещение тегов <script> в HTML значительно эволюционировало вместе с современными практиками веб-разработки. Хотя размещение скриптов в конце <body> остается жизнеспособным подходом, атрибуты async и defer обеспечивают большую гибкость и лучшую производительность. Для приложений jQuery убедитесь, что библиотека загружается перед зависимыми скриптами, и рассмотрите возможность использования современных стратегий загрузки. Лучший подход зависит от вашего конкретного случая использования, требований к производительности и критичности ваших скриптов. Всегда тестируйте и измеряйте влияние ваших решений по размещению скриптов на реальные показатели производительности.