Pre-bundling — это этап предварительной сборки зависимостей, который выполняется Vite перед запуском dev-сервера. Главная задача механизма — ускорение загрузки приложения в браузере и уменьшение количества сетевых запросов при разработке.
Во время pre-bundling Vite анализирует импортируемые зависимости из
node_modules, объединяет их в оптимизированные чанки и
преобразует несовместимые форматы модулей в ESM.
Основной инструмент, выполняющий эту работу, — esbuild.
Многие npm-пакеты состоят из сотен или тысяч файлов. Например:
import { debounce } from 'lodash-es'
Даже один импорт может подтягивать десятки внутренних модулей.
Без предварительной сборки браузер в режиме разработки будет выполнять огромное количество HTTP-запросов:
node_modules/
├── package/
│ ├── module1.js
│ ├── module2.js
│ ├── module3.js
│ └── ...
Это приводит к:
Современный Vite работает через native ESM браузера:
import React from 'react'
Но значительная часть npm-экосистемы всё ещё использует CommonJS:
const React = require('react')
module.exports = React
Браузер не умеет напрямую исполнять CommonJS-модули.
Pre-bundling решает проблему:
Наиболее типичный сценарий:
vite
или:
npm run dev
Во время первого старта Vite:
Если была установлена новая библиотека:
npm install axios
Vite обнаружит изменение dependency graph и повторно выполнит pre-bundling.
Удаление cache также приводит к повторной оптимизации:
rm -rf node_modules/.vite
После следующего запуска dev-сервера pre-bundling будет выполнен заново.
Vite отслеживает:
package-lock.jsonyarn.lockpnpm-lock.yamlЕсли lock-файл изменился, cache dependency optimization инвалидируется.
Изменение конфигурации:
export default defineConfig({
optimizeDeps: {
include: ['some-lib']
}
})
также запускает повторную оптимизацию.
Vite выполняет fast scan исходников.
Например:
import vue from 'vue'
import axios from 'axios'
import { map } from 'lodash-es'
Все bare imports считаются кандидатами для pre-bundling.
Bare import:
import React from 'react'
Не bare import:
import App from './App.vue'
Vite оптимизирует именно зависимости из
node_modules.
Vite анализирует entry points приложения.
Обычно это:
index.html
или:
main.js
main.ts
Во время сканирования строится dependency graph.
После анализа Vite формирует список зависимостей:
[
'react',
'react-dom',
'axios'
]
И передаёт их в esbuild.
esbuild автоматически конвертирует:
const lib = require('lib')
в:
import lib from 'lib'
Множество внутренних файлов библиотеки объединяются в один оптимизированный chunk.
Например:
node_modules/.vite/deps/
Vite создаёт metadata-файлы:
_metadata.json
В них хранится:
По умолчанию:
node_modules/.vite
Пример:
node_modules/.vite/
├── deps/
├── _metadata.json
└── package.json
Исходный импорт:
import React from 'react'
После pre-bundling браузер получает:
import "/node_modules/.vite/deps/react.js"
Vite выбрал esbuild по нескольким причинам.
esbuild написан на Go и значительно быстрее:
esbuild эффективно преобразует:
Pre-bundling должен выполняться практически мгновенно.
Иначе пропадает основное преимущество Vite — быстрый cold start.
После первой оптимизации Vite повторно использует уже собранные зависимости.
Это позволяет запускать dev-сервер почти мгновенно.
Cache сбрасывается при:
Раздел конфигурации:
export default defineConfig({
optimizeDeps: {
}
})
Позволяет управлять pre-bundling вручную.
Принудительное включение зависимости:
export default defineConfig({
optimizeDeps: {
include: ['my-lib']
}
})
Используется когда:
Исключение зависимости:
export default defineConfig({
optimizeDeps: {
exclude: ['big-lib']
}
})
Полезно:
Явное указание entry points:
export default defineConfig({
optimizeDeps: {
entries: ['./src/main.js']
}
})
Позволяет контролировать dependency scan.
Передача параметров напрямую в esbuild:
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
target: 'es2020'
}
}
})
Статические dynamic imports обычно обнаруживаются:
import('./module.js')
Но полностью динамические конструкции:
import(someVariable)
не всегда могут быть проанализированы.
В таком случае dependency приходится добавлять вручную:
optimizeDeps: {
include: ['library']
}
В monorepo возникают особенности.
Пакеты через:
pnpm workspace
npm link
yarn link
часто рассматриваются как source code, а не dependency.
Поэтому Vite может не выполнять pre-bundling автоматически.
Иногда требуется:
optimizeDeps: {
include: ['shared-package']
}
Для SSR используется отдельная логика dependency optimization.
Некоторые зависимости:
CommonJS не поддерживает named exports нативно:
const lib = require('lib')
Но Vite создаёт ESM-обёртки:
import __cjs from '/dep.js'
export const something = __cjs.something
Некоторые пакеты:
Это затрудняет анализ.
Некорректный package.json:
{
"exports": {
".": "./wrong.js"
}
}
может ломать dependency optimization.
Пакеты, использующие:
fs
path
net
не могут работать в браузере.
Во время pre-bundling появляются ошибки.
vite --force
Игнорирует cache и выполняет dependency optimization заново.
Pre-bundling используется только для разработки.
Цели:
Production build выполняет:
Для production используется Rollup.
Предварительно оптимизированные зависимости редко изменяются.
Поэтому:
Во время старта dev-сервера:
Optimizing dependencies...
или:
Pre-bundling dependencies:
Пример:
vite:deps Crawling dependencies...
vite:deps Dependencies bundled in 120ms
Полностью отключать механизм обычно не рекомендуется.
Но возможно:
export default defineConfig({
optimizeDeps: {
disabled: true
}
})
Без pre-bundling:
Современные ESM-пакеты:
export function test() {}
обычно требуют минимальной оптимизации.
Но Vite всё равно может объединять их для уменьшения количества запросов.
Оптимизированные чанки получают hash:
react.js?v=f3sf2a
Это помогает:
Dependency graph Vite содержит:
Pre-bundling уменьшает сложность графа, объединяя множество внутренних модулей библиотеки в один узел.
Исходный код
↓
Dependency scan
↓
Поиск bare imports
↓
esbuild
↓
CommonJS → ESM
↓
Bundling
↓
node_modules/.vite/deps
↓
Dev server
Причины:
Решение:
optimizeDeps: {
include: ['package-name']
}
Симптомы:
Решение:
vite --force
или:
rm -rf node_modules/.vite
Некоторые старые библиотеки используют:
eval()
require(variable)
Такие конструкции плохо анализируются statically.
Pre-bundling — один из ключевых механизмов, благодаря которым Vite обеспечивает:
Без dependency optimization Vite потерял бы значительную часть преимуществ native ESM-подхода.