В чем отличие скомпилированного кода обычного и асинхронного импорта компонентов
Что проверяют
Понимаете ли вы, во что сборщик превращает import и import(), а не только то, что второй «ленивый». Следом обычно спрашивают, почему компонент не выделился в отдельный чанк и что будет, если чанк не загрузится.
Как отвечать
Статический import разбирается на этапе сборки. Модуль попадает в тот же чанк, что и импортирующий код: в webpack он оборачивается в функцию модуля и подключается через __webpack_require__, а в продакшн-сборке Rollup и Vite (и webpack со scope hoisting) склеивают модули в одну область видимости. Код выполняется сразу при загрузке бандла, неиспользуемые экспорты вырезает tree shaking.
Динамический import('./Modal') это точка разделения:
- отдельный файл: сборщик выносит модуль и его уникальные зависимости в свой чанк с хешем в имени;
- webpack: вызов превращается в
__webpack_require__.e(chunkId).then(...). Рантайм вставляет тег<script>, чанк регистрирует себя в глобальном массивеwebpackChunk..., а промис резолвится объектом модуля; - Vite и Rollup: в выходном ESM остаётся нативный
import(), обёрнутый в хелпер__vitePreload, который заранее подгружает CSS и зависимые чанки; - фреймворк: в React это
React.lazy(() => import(...))плюсSuspenseи обязательный default-экспорт, во Vue этоdefineAsyncComponentили ленивые маршруты Vue Router.
Итог для производительности: стартовый бандл меньше, зато при первом показе компонента появляется сетевой запрос. Его сглаживают webpackPrefetch или предзагрузкой по наведению.
Где спотыкаются
Делают lazy, но тот же модуль где-то импортирован статически. Отдельного чанка не будет, Vite об этом предупреждает при сборке.
Не обрабатывают ошибку загрузки. После деплоя старые чанки удалены, открытая вкладка получает ChunkLoadError или ошибку динамического импорта, нужен error boundary с перезагрузкой.
Пишут путь выражением вроде import('./pages/' + name). Сборщик заранее создаёт чанк на каждый подходящий файл в папке.