В режиме разработки Vite использует собственный dev-сервер с нативными ES-модулями и мгновенной трансформацией файлов по запросу браузера. Production-сборка работает по другой модели: вместо потоковой отдачи модулей формируется оптимизированный набор статических файлов. Центральную роль в этом процессе выполняет Rollup.
Именно Rollup отвечает за:
Vite не заменяет Rollup в production-режиме, а выступает надстройкой над ним. Большая часть production-конфигурации Vite фактически преобразуется в Rollup-конфигурацию.
В production-сборке обязанности разделяются следующим образом.
Vite предоставляет:
.env;vite build.Также Vite автоматически формирует Rollup-конфигурацию, избавляя разработчика от необходимости вручную описывать десятки параметров.
Rollup выполняет низкоуровневую сборку:
Именно Rollup производит финальный JavaScript-бандл.
Выбор Rollup обусловлен особенностями архитектуры современных ES-модулей.
Rollup создавался специально для ES Modules. Благодаря этому он умеет:
Tree shaking в Rollup считается одним из наиболее эффективных среди JavaScript bundlers.
Rollup ориентирован не просто на объединение файлов, а на создание компактного и оптимизированного кода.
Сгенерированный output обычно:
Vite может использовать практически всю plugin-инфраструктуру Rollup. Это позволяет:
Команда:
vite build
запускает production pipeline.
Далее Vite выполняет несколько этапов:
dist.Фактически Rollup запускается внутри Vite автоматически.
Одной из ключевых задач Rollup является построение dependency graph.
Например:
// main.js
import { createApp } from './app.js'
import './styles.css'
createApp()
// app.js
import { render } from './render.js'
export function createApp() {
render()
}
Rollup:
На основе dependency graph создается финальный bundle.
Одной из важнейших особенностей Rollup является агрессивный tree shaking.
Исходный модуль:
export function usedFunction() {
console.log('used')
}
export function unusedFunction() {
console.log('unused')
}
Импорт:
import { usedFunction } from './utils.js'
usedFunction()
Во время production-сборки Rollup удалит:
unusedFunction
из итогового бандла.
Rollup анализирует:
Если экспорт:
он исключается из сборки.
Tree shaking работает эффективно только при предсказуемом коде.
export function sum(a, b) {
return a + b
}
window.someGlobalValue = 10
или:
console.log('module loaded')
Такие конструкции считаются side effects.
Если модуль содержит потенциальные side effects, Rollup может сохранить его в output даже при отсутствии используемых экспортов.
Rollup поддерживает автоматическое разделение кода.
button.addEventListener('click', async () => {
const module = await import('./dialog.js')
module.openDialog()
})
Во время сборки Rollup:
dialog.js в отдельный файл;После сборки структура может выглядеть так:
dist/
├── assets/
│ ├── index-AB12.js
│ ├── dialog-CD34.js
│ └── index-EF56.css
Главный bundle загружается сразу, а dialog-CD34.js —
только при вызове import().
Rollup умеет выделять сторонние зависимости в отдельные чанки.
Например:
import lodash from 'lodash'
import axios from 'axios'
Vite может сформировать:
vendor.js
с внешними библиотеками.
Это улучшает:
Vite позволяет напрямую управлять Rollup chunk splitting.
Пример:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'axios']
}
}
}
}
})
В этом случае Rollup создаст отдельный chunk:
vendor.[hash].js
Rollup участвует и в обработке CSS.
Vite извлекает CSS из модулей:
import './style.css'
После чего Rollup:
Rollup умеет работать с:
Пример:
import logo from './logo.png'
Во время production-сборки:
dist/assets;Результат:
const logo = "/assets/logo-8d72f.png"
Rollup генерирует content hash:
app-83hf72.js
Это необходимо для:
При изменении содержимого hash меняется автоматически.
После завершения основной сборки Vite запускает minification.
В современных версиях Vite обычно используются:
Rollup подготавливает код к минимизации:
Rollup отвечает за финальную структуру dist.
Пример:
dist/
├── index.html
├── assets/
│ ├── index-A1B2.js
│ ├── vendor-C3D4.js
│ ├── index-E5F6.css
│ └── logo-G7H8.svg
Все связи между файлами автоматически обновляются.
Rollup умеет создавать sourcemaps:
build: {
sourcemap: true
}
В результате:
index.js.map
содержит соответствие между production-кодом и исходными файлами.
Это необходимо для:
Vite поддерживает Rollup plugins через:
build: {
rollupOptions: {
plugins: []
}
}
Пример:
import visualizer from 'rollup-plugin-visualizer'
export default defineConfig({
build: {
rollupOptions: {
plugins: [visualizer()]
}
}
})
Плагин может:
Rollup поддерживает разные форматы сборки:
Vite чаще всего использует ESM для web-приложений, однако library mode позволяет генерировать разные форматы.
Пример:
build: {
lib: {
entry: 'src/index.js',
name: 'MyLibrary'
}
}
Rollup создаст библиотечные bundles.
В режиме библиотеки Rollup становится особенно важным.
Он отвечает за:
Пример external dependencies:
rollupOptions: {
external: ['vue']
}
Тогда Vue не попадет в bundle.
Rollup может объединять импорты:
Исходный код:
import { ref } from 'vue'
import { reactive } from 'vue'
После оптимизации:
import { ref, reactive } from 'vue'
Это уменьшает размер output.
Rollup активно использует static analysis.
Он анализирует:
Благодаря статическому анализу достигается:
Несмотря на высокую эффективность, Rollup имеет ограничения.
Проблемные конструкции:
require(variable)
или:
globalThis[someName]()
Статический анализ таких выражений затруднен.
Rollup максимально эффективен именно с ES Modules. Legacy CommonJS-пакеты требуют дополнительной обработки.
В production-сборке Vite одновременно использует:
Разделение задач выглядит так:
| Инструмент | Основная роль |
|---|---|
| Rollup | Bundling и graph analysis |
| esbuild | Трансформация и minification |
Rollup управляет архитектурой сборки, а esbuild обеспечивает высокую скорость отдельных операций.
Архитектура Vite идеально сочетается с Rollup по нескольким причинам:
В результате production-сборки Vite обычно: