import.meta.glob — один из ключевых механизмов Vite, позволяющий работать с наборами файлов как с единой динамической структурой, без ручного перечисления импортов. Этот API строится поверх возможностей сборщика и файловой системы, предоставляя декларативный способ загрузки модулей по шаблону пути.
Glob-паттерны используются для массового сопоставления файлов по маске. Вместо явного перечисления:
import a from './modules/a.js'
import b from './modules/b.js'
import c from './modules/c.js'
Vite позволяет описать правило:
const modules = import.meta.glob('./modules/*.js')
Результатом становится объект, где ключами выступают пути к файлам, а значениями — функции, возвращающие динамический импорт модуля.
{
'./modules/a.js': () => import('./modules/a.js'),
'./modules/b.js': () => import('./modules/b.js')
}
Такой подход переносит ответственность за обнаружение файлов на этап сборки.
import.meta.glob анализируется Vite на этапе компиляции. В отличие от runtime-логики, glob не выполняется в браузере как строковое сопоставление. Вместо этого:
Это означает, что результат известен до выполнения приложения.
const modules = import.meta.glob('./pages/*.js')
Каждое значение — ленивый импорт:
const mod = await modules['./pages/home.js']()
Фактически используется динамическая загрузка, но управляемая через единый индекс.
По умолчанию import.meta.glob возвращает функции. Однако можно изменить поведение, заставив Vite сразу импортировать все модули:
const modules = import.meta.glob('./pages/*.js', { eager: true })
В этом случае результат становится аналогичен обычным импортам:
{
'./pages/home.js': { default: ... },
'./pages/about.js': { default: ... }
}
Разница принципиальна:
Ленивый режим позволяет строить асинхронные загрузчики:
const modules = import.meta.glob('./services/*.js')
async function loadService(name) {
const modulePath = `./services/${name}.js`
const module = await modules[modulePath]()
return module.default
}
Такой подход часто используется в архитектурах с плагинной структурой.
Каждый импортируемый модуль сохраняет свою структуру экспорта:
// service.js
export default function service() {}
export const version = '1.0'
При загрузке:
const modules = import.meta.glob('./*.js')
const mod = await modules['./service.js']()
mod.default()
mod.version
Vite не трансформирует структуру экспорта, а лишь оборачивает импорт.
import.meta.glob поддерживает второй параметр — transform-функцию:
const modules = import.meta.glob('./modules/*.js', {
import: 'default'
})
В этом случае загружается только default-экспорт:
{
'./modules/a.js': () => import('./modules/a.js').then(m => m.default)
}
Это упрощает работу, когда требуется только основной экспорт.
Можно комбинировать eager и import:
const modules = import.meta.glob('./modules/*.js', {
eager: true,
import: 'default'
})
Результат — объект с готовыми значениями без функций-обёрток.
Glob-паттерны поддерживают расширенные выражения:
import.meta.glob('./modules/**/*.js')
Поддерживаются:
**Пример исключения:
import.meta.glob(['./modules/*.js', '!./modules/test.js'])
Один из наиболее частых кейсов — автоматическая регистрация страниц:
const pages = import.meta.glob('./pages/*.vue')
const routes = Object.entries(pages).map(([path, loader]) => {
return {
path: path
.replace('./pages', '')
.replace('.vue', ''),
component: loader
}
})
Это позволяет отказаться от ручного описания маршрутов.
const views = import.meta.glob('./views/*.vue')
export default {
async component(name) {
const mod = await views[`./views/${name}.vue`]()
return mod.default
}
}
const components = import.meta.glob('./components/*.jsx')
export function loadComponent(name) {
return components[`./components/${name}.jsx`]().then(m => m.default)
}
Vite позволяет импортировать не только JS-модули:
const dataFiles = import.meta.glob('./data/*.json')
Или даже текстовые ресурсы при соответствующей настройке:
const texts = import.meta.glob('./content/*.txt')
Важно учитывать фундаментальное ограничение: import.meta.glob не является динамическим в runtime.
Следующий код не работает:
const pattern = './modules/*.js'
import.meta.glob(pattern)
Шаблон должен быть строковым литералом, чтобы Vite мог обработать его на этапе сборки.
В Webpack использовался require.context, который решал похожую задачу:
Ключевое отличие:
Это влияет на предсказуемость и оптимизацию.
Lazy-режим glob напрямую влияет на разбиение кода:
const modules = import.meta.glob('./features/*.js')
Каждый модуль становится отдельным chunk’ом. Это позволяет:
Eager-режим, наоборот, объединяет загрузку в один стартовый поток.
Glob-импорты часто используются для:
Пример реестра команд:
const commands = import.meta.glob('./commands/*.js')
export async function runCommand(name, ...args) {
const mod = await commands[`./commands/${name}.js`]()
return mod.execute(...args)
}
Такая модель устраняет необходимость ручной регистрации модулей.
TypeScript корректно выводит типы при использовании glob:
const modules = import.meta.glob('./*.ts')
Однако при сложных структурах может потребоваться явная типизация:
const modules: Record<string, () => Promise<any>> =
import.meta.glob('./*.ts')
Это особенно важно при строгих архитектурах.
import.meta.glob формирует статический индекс файлов на этапе сборки, превращая структуру проекта в доступную карту модулей. Это позволяет отказаться от ручных импортов, выстраивать динамические системы загрузки и оптимизировать разбиение кода без дополнительных инструментов.