Где и с какими атрибутами лучше подключать скрипт входной точки single page application без серверного рендеринга?
Что проверяют
Знаете ли вы, как браузер загружает и выполняет скрипты и чем отличаются defer, async и type="module". Для SPA без SSR это особенно важно: пока бандл не выполнился, пользователь видит пустой <div id="root">. Следом спрашивают, что будет, если поставить async на входную точку.
Как отвечать
Короткий ответ: в <head>, с type="module" или defer. Именно так делает Vite: <script type="module" crossorigin src="/assets/index-[hash].js"> в <head> плюс <link rel="modulepreload"> для зависимых чанков.
Почему так:
- место: в
<head>браузер находит скрипт сразу и начинает загрузку параллельно с разбором HTML. Совет «ставьте перед</body>» пришёл из времён, когда атрибутов не было и скрипт блокировал разбор. В SPA без SSR он ничего не даёт: показывать до выполнения JS всё равно нечего; defer: загрузка не блокирует парсер, выполнение после разбора документа, строго в порядке подключения, до событияDOMContentLoaded. К моменту запуска#rootуже есть в DOM;type="module": модульные скрипты отложены по умолчанию,deferк ним добавлять не нужно. Они загружаются в режиме CORS, поэтому CDN с бандлом должен отдавать заголовки CORS;async: выполнение сразу по готовности, в любом порядке и, возможно, до окончания разбора. Подходит для независимых скриптов вроде аналитики, но не для входной точки.
Дополнительно: хеш в имени файла и долгий Cache-Control для бандла, integrity при загрузке с чужого домена, а внутрь #root можно положить лёгкий скелетон, который приложение заменит при монтировании.
Где спотыкаются
Ставят async на входную точку. Если скрипт стоит в <head> до #root, приложение иногда стартует раньше, чем появился контейнер, и ошибка воспроизводится нестабильно, чаще всего на закэшированном бандле.
Пишут одновременно async и defer, считая, что получат лучшее из двух. Браузер применит async.
Думают, что defer работает для встроенного скрипта без src. Не работает: у классического inline-скрипта атрибут игнорируется, отложенным будет только встроенный type="module".