Как загружать скрипт, не блокируя отрисовку страницы?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Неблокирующая загрузка скриптов
По умолчанию <script> тег блокирует отрисовку страницы. Браузер должен:
- Загрузить скрипт
- Распарсить его
- Выполнить
- Только потом продолжить отрисовку DOM
Это может добавить секунды задержки. Есть несколько способов избежать этого.
1. Атрибут async
<script async src="/analytics.js"></script>
Скрипт загружается параллельно с отрисовкой, выполняется как только готов. Порядок выполнения не гарантирован — если у вас несколько async скриптов, они выполнятся в непредсказуемом порядке.
Когда использовать:
- Аналитика (Google Analytics, Sentry)
- Трекинг пикселей
- Независимые сервисы
2. Атрибут defer
<script defer src="/app.js"></script>
Скрипт загружается параллельно, но выполняется только после полного парса DOM. Порядок выполнения гарантирован.
Когда использовать:
- Основной скрипт приложения
- Библиотеки которые зависят друг от друга
- Когда порядок выполнения важен
<script defer src="/vendor.js"></script>
<script defer src="/app.js"></script>
<script defer src="/init.js"></script>
3. Динамическая загрузка скриптов
const script = document.createElement('script');
script.src = '/my-script.js';
script.async = true;
script.onload = () => {
console.log('Script loaded!');
myFunction();
};
script.onerror = () => {
console.error('Failed to load script');
};
document.head.appendChild(script);
Это полезно когда:
- Нужно загрузить скрипт на основе условий
- Нужно отслеживать загрузку
- Нужно загрузить скрипт позже (например, при взаимодействии пользователя)
4. Атрибут module (ES Modules)
<script type="module" src="/app.js"></script>
Автоматически defer для модулей. CORS требуется. Область видимости — изолирована.
5. Resource Hints
<link rel="dns-prefetch" href="https://cdn.example.com" />
<link rel="preconnect" href="https://cdn.example.com" />
<link rel="prefetch" href="/next-page-script.js" />
<link rel="preload" href="/critical-script.js" as="script" />
6. Worker Threads
Если скрипт содержит долгие вычисления, переноси их в Web Worker для неблокирующего исполнения:
const worker = new Worker('/worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
7. Стратегия для разных типов скриптов
<script defer src="/bundle.js"></script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
<script async src="https://cdn.segment.com/analytics.js"></script>
Сравнение способов
| Метод | Блокирует | Порядок | Когда выполняется |
|---|---|---|---|
| По умолчанию | Да | Гарантирован | Сразу при парсе |
| async | Нет | Не гарантирован | АСAP |
| defer | Нет | Гарантирован | После парса DOM |
| Динамическая | Нет | Контролируемо | На demand |
| Module | Нет | Гарантирован | После парса |
Best Practice
Для современных приложений (Next.js, React):
<script type="module" defer src="/app.js"></script>
Модули автоматически:
- Являются async по отношению к HTML парсингу
- Гарантирована очередность выполнения
- Безопаснее (изолированная область видимости)
Это основной способ избежать блокировки отрисовки в современных браузерах. Используй defer для основного кода приложения, async для независимых сервисов вроде аналитики, и динамическую загрузку для опциональных скриптов которые загружаются на demand.