Glob-импорты через import.meta.glob

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

import.meta.glob анализируется Vite на этапе компиляции. В отличие от runtime-логики, glob не выполняется в браузере как строковое сопоставление. Вместо этого:

  1. Vite находит все файлы, соответствующие шаблону
  2. Генерирует список модулей
  3. Создаёт объект-реестр
  4. Подставляет динамические import() для каждого совпадения

Это означает, что результат известен до выполнения приложения.

Базовый синтаксис

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: ... }
}

Разница принципиальна:

  • без eager — lazy loading
  • с eager — синхронная загрузка на этапе инициализации

Использование с async/await

Ленивый режим позволяет строить асинхронные загрузчики:

const modules = import.meta.glob('./services/*.js')

async function loadService(name) {
  const modulePath = `./services/${name}.js`
  const module = await modules[modulePath]()
  return module.default
}

Такой подход часто используется в архитектурах с плагинной структурой.

Работа с default и named exports

Каждый импортируемый модуль сохраняет свою структуру экспорта:

// 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 не трансформирует структуру экспорта, а лишь оборачивает импорт.

glob с функцией обработки импорта

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
  }
})

Это позволяет отказаться от ручного описания маршрутов.

Использование с Vue и React

Vue

const views = import.meta.glob('./views/*.vue')

export default {
  async component(name) {
    const mod = await views[`./views/${name}.vue`]()
    return mod.default
  }
}

React

const components = import.meta.glob('./components/*.jsx')

export function loadComponent(name) {
  return components[`./components/${name}.jsx`]().then(m => m.default)
}

glob с JSON и другими ресурсами

Vite позволяет импортировать не только JS-модули:

const dataFiles = import.meta.glob('./data/*.json')

Или даже текстовые ресурсы при соответствующей настройке:

const texts = import.meta.glob('./content/*.txt')

Статическая природа glob

Важно учитывать фундаментальное ограничение: import.meta.glob не является динамическим в runtime.

Следующий код не работает:

const pattern = './modules/*.js'
import.meta.glob(pattern)

Шаблон должен быть строковым литералом, чтобы Vite мог обработать его на этапе сборки.

Отличие от require.context

В Webpack использовался require.context, который решал похожую задачу:

  • require.context — runtime API Webpack
  • import.meta.glob — compile-time API Vite

Ключевое отличие:

  • glob полностью статичен на этапе сборки
  • require.context имеет runtime-объект контекста

Это влияет на предсказуемость и оптимизацию.

Производительность и code splitting

Lazy-режим glob напрямую влияет на разбиение кода:

const modules = import.meta.glob('./features/*.js')

Каждый модуль становится отдельным chunk’ом. Это позволяет:

  • уменьшить initial bundle
  • загружать код по требованию
  • распределять нагрузку

Eager-режим, наоборот, объединяет загрузку в один стартовый поток.

Практическая архитектурная модель

Glob-импорты часто используются для:

  • автозагрузки плагинов
  • динамических роутов
  • модульных сервисов
  • CMS-подобных структур
  • системы команд или действий

Пример реестра команд:

const commands = import.meta.glob('./commands/*.js')

export async function runCommand(name, ...args) {
  const mod = await commands[`./commands/${name}.js`]()
  return mod.execute(...args)
}

Такая модель устраняет необходимость ручной регистрации модулей.

Ограничения и особенности

  • поддерживаются только статические шаблоны
  • путь должен быть известен на этапе сборки
  • нельзя использовать переменные внутри glob
  • результат зависит от файловой структуры проекта
  • eager-загрузка может существенно увеличить стартовый вес приложения

Взаимодействие с TypeScript

TypeScript корректно выводит типы при использовании glob:

const modules = import.meta.glob('./*.ts')

Однако при сложных структурах может потребоваться явная типизация:

const modules: Record<string, () => Promise<any>> =
  import.meta.glob('./*.ts')

Это особенно важно при строгих архитектурах.

Итоговая модель поведения glob-импортов

import.meta.glob формирует статический индекс файлов на этапе сборки, превращая структуру проекта в доступную карту модулей. Это позволяет отказаться от ручных импортов, выстраивать динамические системы загрузки и оптимизировать разбиение кода без дополнительных инструментов.