Одной из ключевых особенностей Vite является высокая скорость запуска dev-сервера. Эта скорость достигается не только за счёт использования native ES Modules в браузере, но и благодаря механизму предварительной обработки зависимостей — pre-bundling.
Pre-bundling — это этап предварительной сборки npm-зависимостей перед
запуском приложения в режиме разработки. Во время этого процесса Vite
анализирует подключённые пакеты из node_modules,
преобразует их в оптимизированный формат и кэширует результат.
Основные задачи pre-bundling:
Современные браузеры умеют работать с ES Modules напрямую:
import { createApp } from 'vue'
Однако при использовании npm-зависимостей возникает несколько сложностей.
Некоторые библиотеки состоят из сотен или тысяч файлов.
Например:
import lodash from 'lodash'
Внутри lodash может существовать огромное количество
импортов:
import baseClone from './baseClone.js'
import Stack from './Stack.js'
import isArray from './isArray.js'
Если браузер будет загружать каждый файл отдельно:
Многие старые npm-пакеты написаны в формате CommonJS:
const axios = require('axios')
module.exports = axios
Браузер не понимает CommonJS напрямую.
Он ожидает ES Modules:
import axios from 'axios'
Следовательно, Vite должен:
Именно этим занимается pre-bundling.
При первом запуске:
npm run dev
Vite выполняет несколько этапов.
Сначала анализируются импортируемые зависимости:
import vue from 'vue'
import axios from 'axios'
import dayjs from 'dayjs'
Vite строит граф зависимостей и определяет:
node_modules;Для pre-bundling Vite использует esbuild.
esbuild выполняет:
Пример условного преобразования:
Исходный пакет:
const lib = require('./lib')
module.exports = {
lib
}
После обработки:
import lib from './lib.js'
export {
lib
}
esbuild написан на Go и работает значительно быстрее
большинства JavaScript-бандлеров.
Скорость особенно важна именно для dev-режима, где критичны:
Pre-bundling должен выполняться практически незаметно для разработчика.
После обработки Vite создаёт специальный кэш:
node_modules/.vite
Пример содержимого:
node_modules/.vite/deps/
Там находятся:
Пример:
chunk-ABCD1234.js
vue.js
axios.js
_metadata.json
После первой оптимизации Vite не запускает pre-bundling повторно без необходимости.
При следующем запуске:
npm run dev
Vite проверяет:
package.json;Если изменений нет — используется готовый кэш.
Это резко ускоряет повторный старт dev-сервера.
_metadata.jsonVite хранит служебную информацию о зависимостях.
Пример структуры:
{
"optimized": {
"vue": {
"src": "../. ./vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js"
}
}
}
Этот файл позволяет:
По умолчанию Vite автоматически анализирует импорты.
Например:
import react from 'react'
import reactDOM from 'react-dom'
Эти пакеты будут автоматически pre-bundled.
Vite также способен анализировать некоторые динамические импорты:
const module = await import('./module.js')
Но динамические зависимости сложнее для анализа.
Если пакет невозможно определить заранее, может понадобиться ручная настройка.
Для управления pre-bundling используется раздел:
export default defineConfig({
optimizeDeps: {
}
})
Позволяет принудительно добавить пакет в pre-bundling.
export default defineConfig({
optimizeDeps: {
include: ['some-large-lib']
}
})
Полезно в случаях:
Исключает пакет из pre-bundling.
export default defineConfig({
optimizeDeps: {
exclude: ['my-lib']
}
})
Используется редко, но бывает полезно:
Предположим, библиотека содержит 500 внутренних модулей.
Без pre-bundling браузер выполнит:
После pre-bundling:
lodash.js
Браузер загружает один оптимизированный файл вместо сотен мелких модулей.
Hot Module Replacement напрямую зависит от скорости обработки модулей.
Если зависимости уже оптимизированы:
Важно понимать, что pre-bundling работает только в dev-режиме.
Используется:
Используется production-сборка через Rollup:
vite build
В production:
Pre-bundling и production bundle — разные процессы.
Традиционные bundler-подходы требуют полной пересборки приложения.
Например:
webpack -> bundle everything
Vite работает иначе:
Это уменьшает объём работы dev-сервера.
import React from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'
Vite обнаруживает:
Создаются:
react.js
react-dom.js
axios.js
Файлы сохраняются:
node_modules/.vite/deps/
Браузер получает уже оптимизированные зависимости.
После команды:
npm install dayjs
Vite обнаруживает изменение lock-файла.
При следующем запуске:
npm run dev
pre-bundling выполняется заново только для изменённых зависимостей.
Иногда требуется очистить кэш.
Для этого используется:
vite --force
Или:
npm run dev -- --force
Vite:
Некоторые библиотеки имеют нестандартную структуру:
if (process.env.NODE_ENV) {
require('./prod')
}
Статический анализ становится сложнее.
Проблемный код:
require(someVariable)
esbuild не может заранее определить модуль.
Некоторые библиотеки одновременно используют:
module.exports
и
export default
Это может вызывать ошибки совместимости.
В monorepo-структурах зависимости могут находиться вне стандартного
node_modules.
Пример:
packages/
apps/
shared/
В таких случаях Vite иногда требует ручного указания:
optimizeDeps: {
include: ['shared-lib']
}
Экосистема React традиционно содержит большое количество CommonJS-пакетов.
Например:
Pre-bundling позволяет:
Даже при использовании HTTP/2 большое количество модулей остаётся проблемой.
Причины:
Pre-bundling снижает эти издержки.
После оптимизации импорт:
import axios from 'axios'
фактически превращается во внутренний путь:
/node_modules/.vite/deps/axios.js
Браузер получает уже подготовленный ESM-файл.
Vite строит граф зависимостей приложения.
Pre-bundling уменьшает сложность этого графа:
500 внутренних модулей
↓
1 оптимизированный модуль
Это улучшает:
Cold start — первый запуск dev-сервера.
Именно в этот момент pre-bundling даёт наибольший эффект.
Без оптимизации:
С pre-bundling:
Pre-bundling является фундаментальной частью архитектуры Vite.
Именно он позволяет совместить:
Без механизма предварительной оптимизации использование ES Modules в крупных frontend-проектах было бы значительно менее эффективным.