Как загружать скрипт, не блокируя загрузку страницы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Неблокирующая загрузка скриптов: async и defer
Дефолтное поведение тега <script> в HTML — синхронная загрузка и выполнение, что блокирует парсинг DOM и рендеринг страницы. Это серьезно замедляет Time to Interactive. Есть несколько стратегий решения.
Attribute: defer
Атрибут defer загружает скрипт параллельно с парсингом DOM, но выполняет его только после полной загрузки документа. Это идеально для скриптов, которые зависят от DOM-элементов.
<!-- Загружается в фоне, выполняется ПОСЛЕ парсинга DOM -->
<script src="app.js" defer></script>
<!-- Несколько скриптов с defer выполняются в порядке исходного кода -->
<script src="library.js" defer></script>
<script src="app.js" defer></script>
<!-- library.js выполнится ДО app.js -->
Attribute: async
async загружает скрипт параллельно и выполняет его сразу после загрузки, не дожидаясь парсинга DOM. Скрипты выполняются в произвольном порядке. Используй для независимых скриптов (аналитика, трекеры).
<!-- Загружается и выполняется в любой момент -->
<script src="analytics.js" async></script>
<!-- Несколько async скриптов — не гарантирован порядок -->
<script src="tracker1.js" async></script>
<script src="tracker2.js" async></script>
Динамическая загрузка с помощью JavaScript
Для более гибкого контроля можешь динамически загружать скрипты:
// Загрузка без блокировки
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Использование
await loadScript('app.js');
console.log('Script loaded!');
// Загрузка нескольких скриптов параллельно
Promise.all([
loadScript('lib1.js'),
loadScript('lib2.js'),
loadScript('lib3.js')
]).then(() => {
console.log('All scripts loaded!');
});
Динамическая загрузка с контролем порядка
Если нужен конкретный порядок выполнения:
// Последовательная загрузка
async function loadScriptsInOrder(scripts) {
for (const src of scripts) {
await loadScript(src);
}
console.log('All scripts loaded in order!');
}
loadScriptsInOrder([
'jquery.js',
'bootstrap.js',
'app.js'
]);
// Или более просто с async/await
const loadScriptAsync = (src) =>
new Promise((resolve) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve();
document.head.appendChild(script);
});
await loadScriptAsync('dependency.js');
await loadScriptAsync('app.js');
Modern approach: ES Modules
С поддержкой модулей в браузере можешь использовать native imports:
<!-- Автоматически асинхронно, типо defer -->
<script type="module" src="app.js"></script>
// app.js
import { init } from './modules/init.js';
import { setupUI } from './modules/ui.js';
await init();
await setupUI();
Сравнение стратегий
синхронный <script>:
Время загрузки: ████████ БЛОКИРУЕТ парсинг
Используй: Критичные скрипты нужные ДО рендера
<script defer>:
Время загрузки: ████ загружается в фоне
Выполнение: ████ после парсинга DOM
Используй: Основные скрипты (app.js, analytics)
<script async>:
Время загрузки: ████ загружается в фоне
Выполнение: ████ сразу после загрузки
Используй: независимые скрипты (трекеры)
Best Practice для Production
<head>
<!-- Критичные стили -->
<link rel="stylesheet" href="critical.css">
</head>
<body>
<div id="app"></div>
<!-- Основной скрипт приложения -->
<script src="app.js" defer></script>
<!-- Аналитика -->
<script src="analytics.js" async></script>
<!-- Реклама (может задержаться) -->
<script src="ads.js" async></script>
</body>
Вывод: используй defer для основных скриптов, async для независимых, и избегай синхронной загрузки. Это значительно улучшит Core Web Vitals и пользовательский опыт.