Динамический импорт в Vite основан на стандартной спецификации
ECMAScript и реализуется через функцию import(), которая
возвращает Promise с загружаемым модулем. В контексте Vite
этот механизм тесно связан с разбиением кода на чанки (code splitting),
где каждый динамически импортируемый модуль выделяется в отдельный файл
сборки.
В момент сборки Vite анализирует граф зависимостей приложения и выделяет статические импорты в основной бандл, а динамические — в отдельные чанки. Такой подход снижает первоначальный размер загружаемого JavaScript и переносит загрузку части логики на момент реальной необходимости.
const module = await import('./modules/analytics.js');
module.init();
Каждый вызов import() становится точкой разделения кода.
В результате формируется отдельный файл, который загружается
асинхронно.
Ключевая особенность заключается в том, что Vite использует нативную поддержку ES Modules в браузере в режиме разработки и Rollup в production-сборке, где происходит финальное разделение чанков.
Статический импорт выполняется на этапе загрузки модуля:
import { formatDate } from './utils/date.js';
Такой код всегда попадает в основной бандл.
Динамический импорт выполняется во время выполнения:
const { formatDate } = await import('./utils/date.js');
Разница заключается в моменте разрешения зависимости: статические импорты обрабатываются заранее, динамические — по требованию.
Статический импорт обеспечивает предсказуемость графа зависимостей, динамический — управляемую ленивую загрузку.
Vite делегирует финальное разбиение модулей Rollup. Каждый динамический импорт становится отдельным chunk-файлом, который подключается через сетевой запрос.
Пример структуры после сборки:
dist/
assets/
index-xxxxx.js
vendor-xxxxx.js
analytics-xxxxx.js
Модуль analytics.js, загруженный через
import(), выделяется в отдельный файл
analytics-xxxxx.js.
При этом Vite автоматически управляет хешированием файлов, что обеспечивает кэширование на уровне браузера.
Ленивая загрузка реализуется через динамический импорт в обработчиках событий, маршрутах или условиях выполнения.
button.addEventListener('click', async () => {
const { openModal } = await import('./ui/modal.js');
openModal();
});
Загрузка модуля происходит только при взаимодействии, что уменьшает начальный вес приложения.
Такая модель особенно эффективна для:
В одностраничных приложениях маршрутизация часто становится основным источником разбиения кода. Vite не навязывает конкретный роутер, но типовые решения используют динамический импорт для маршрутов.
import { lazy, Suspense } from 'react';
import { createBrowserRouter } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home.jsx'));
const Dashboard = lazy(() => import('./pages/Dashboard.jsx'));
const router = createBrowserRouter([
{
path: '/',
element: (
<Suspense fallback={<div>Loading...</div>}>
<Home />
</Suspense>
)
},
{
path: '/dashboard',
element: (
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
)
}
]);
export default router;
Каждый компонент страницы становится отдельным чанком, который загружается только при переходе на маршрут.
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
];
export default createRouter({
history: createWebHistory(),
routes
});
Vue Router поддерживает ленивую загрузку через функцию, возвращающую
import().
Vite и Rollup позволяют контролировать объединение динамических
импортов через именованные чанки и комментарии
webpackChunkName (в контексте совместимости, хотя
фактически работает Rollup-аналог).
const module = await import(
/* chunkName: "charts" */
'./charts/chart.js'
);
Несколько динамических импортов с одинаковым смыслом могут быть объединены в один chunk при определённой конфигурации Rollup.
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('charts')) {
return 'charts';
}
}
}
}
}
};
Такой подход позволяет управлять структурой выходных файлов и оптимизировать кеширование.
Vite поддерживает механизм hint-загрузки через стандартные атрибуты браузера и динамическое управление импортами.
const modulePromise = import('./heavy-module.js');
// параллельная подготовка без ожидания результата
modulePromise.then((module) => {
module.init();
});
В HTML также могут использоваться подсказки:
<link rel="modulepreload" href="/assets/heavy-module.js">
Это снижает задержки при фактическом вызове
import().
Асинхронная природа import() влияет на архитектуру
управления состоянием. Модули, загружаемые лениво, требуют учёта
состояния загрузки.
let chartModule = null;
async function loadChart() {
if (!chartModule) {
chartModule = await import('./chart.js');
}
return chartModule;
}
Такой паттерн предотвращает повторные загрузки и обеспечивает кеширование на уровне рантайма.
Одной из частых проблем становится некорректный путь к модулю. Vite
требует, чтобы выражение import() было статически
анализируемым в пределах базового пути.
Неподдерживаемый вариант:
const module = await import(`./modules/${name}.js`);
Такой код ограничивает возможности статического анализа и может привести к включению лишних файлов или ошибкам сборки.
Корректный подход — использование явных маппингов:
const modules = {
analytics: () => import('./modules/analytics.js'),
reports: () => import('./modules/reports.js')
};
const module = await modules[name]();
Эффективность динамического импорта усиливается при сочетании с:
Vite заранее обрабатывает зависимости через pre-bundling, что уменьшает накладные расходы при первом динамическом импорте.
Ленивая загрузка маршрутов часто комбинируется с уровневой архитектурой:
route.onEnter(async () => {
const feature = await import('./features/reports/index.js');
feature.mount();
});
Такой подход позволяет масштабировать приложение без увеличения стартового бандла.
Динамический импорт напрямую влияет на метрики загрузки:
Vite обеспечивает предсказуемую модель разделения кода, где каждый
import() становится явной границей между синхронной и
асинхронной частью приложения.