Роль Rollup в режиме production

В режиме разработки Vite использует собственный dev-сервер с нативными ES-модулями и мгновенной трансформацией файлов по запросу браузера. Production-сборка работает по другой модели: вместо потоковой отдачи модулей формируется оптимизированный набор статических файлов. Центральную роль в этом процессе выполняет Rollup.

Именно Rollup отвечает за:

  • построение графа зависимостей;
  • объединение модулей;
  • tree shaking;
  • разделение кода;
  • генерацию чанков;
  • преобразование форматов;
  • финальную оптимизацию структуры сборки;
  • обработку импортов и экспортов;
  • работу production-плагинов.

Vite не заменяет Rollup в production-режиме, а выступает надстройкой над ним. Большая часть production-конфигурации Vite фактически преобразуется в Rollup-конфигурацию.


Разделение ответственности между Vite и Rollup

В production-сборке обязанности разделяются следующим образом.

За что отвечает Vite

Vite предоставляет:

  • единый интерфейс конфигурации;
  • систему плагинов;
  • интеграцию с TypeScript;
  • обработку CSS;
  • работу с .env;
  • поддержку JSX и framework-плагинов;
  • оптимизацию ассетов;
  • pre-bundling зависимостей;
  • запуск команды vite build.

Также Vite автоматически формирует Rollup-конфигурацию, избавляя разработчика от необходимости вручную описывать десятки параметров.

За что отвечает Rollup

Rollup выполняет низкоуровневую сборку:

  • анализирует import/export;
  • строит dependency graph;
  • вычисляет неиспользуемый код;
  • удаляет dead code;
  • создает production-чанки;
  • генерирует output-файлы;
  • формирует sourcemaps;
  • минимизирует дублирование модулей;
  • оптимизирует загрузку.

Именно Rollup производит финальный JavaScript-бандл.


Почему Vite использует Rollup

Выбор Rollup обусловлен особенностями архитектуры современных ES-модулей.

Эффективная работа с ESM

Rollup создавался специально для ES Modules. Благодаря этому он умеет:

  • точно анализировать зависимости;
  • определять неиспользуемые экспорты;
  • агрессивно удалять лишний код;
  • строить минимальные production-бандлы.

Tree shaking в Rollup считается одним из наиболее эффективных среди JavaScript bundlers.

Высокое качество output-файлов

Rollup ориентирован не просто на объединение файлов, а на создание компактного и оптимизированного кода.

Сгенерированный output обычно:

  • меньше по размеру;
  • содержит меньше оберток;
  • проще для minifier;
  • быстрее выполняется браузером.

Богатая plugin-экосистема

Vite может использовать практически всю plugin-инфраструктуру Rollup. Это позволяет:

  • подключать legacy-плагины;
  • настраивать custom output;
  • модифицировать процесс сборки;
  • интегрировать нестандартные пайплайны.

Как Vite запускает Rollup

Команда:

vite build

запускает production pipeline.

Далее Vite выполняет несколько этапов:

  1. Загружает конфигурацию.
  2. Активирует плагины.
  3. Анализирует entry points.
  4. Формирует Rollup config.
  5. Передает управление Rollup.
  6. Получает готовые чанки.
  7. Обрабатывает ассеты.
  8. Генерирует итоговую директорию 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.


Tree Shaking

Одной из важнейших особенностей Rollup является агрессивный tree shaking.

Пример удаления неиспользуемого кода

Исходный модуль:

export function usedFunction() {
    console.log('used')
}

export function unusedFunction() {
    console.log('unused')
}

Импорт:

import { usedFunction } from './utils.js'

usedFunction()

Во время production-сборки Rollup удалит:

unusedFunction

из итогового бандла.


Как работает tree shaking

Rollup анализирует:

  • export declarations;
  • import bindings;
  • side effects;
  • использование символов;
  • re-export цепочки.

Если экспорт:

  • нигде не импортируется;
  • не имеет side effects;
  • не используется косвенно,

он исключается из сборки.


Side Effects и ограничения tree shaking

Tree shaking работает эффективно только при предсказуемом коде.

Хорошо оптимизируемый код

export function sum(a, b) {
    return a + b
}

Проблемный код

window.someGlobalValue = 10

или:

console.log('module loaded')

Такие конструкции считаются side effects.

Если модуль содержит потенциальные side effects, Rollup может сохранить его в output даже при отсутствии используемых экспортов.


Code Splitting

Rollup поддерживает автоматическое разделение кода.

Динамический импорт

button.addEventListener('click', async () => {
    const module = await import('./dialog.js')

    module.openDialog()
})

Во время сборки Rollup:

  • создает отдельный chunk;
  • выносит dialog.js в отдельный файл;
  • генерирует lazy-loading загрузку.

Как выглядит результат code splitting

После сборки структура может выглядеть так:

dist/
├── assets/
│   ├── index-AB12.js
│   ├── dialog-CD34.js
│   └── index-EF56.css

Главный bundle загружается сразу, а dialog-CD34.js — только при вызове import().


Vendor Chunks

Rollup умеет выделять сторонние зависимости в отдельные чанки.

Например:

import lodash from 'lodash'
import axios from 'axios'

Vite может сформировать:

vendor.js

с внешними библиотеками.

Это улучшает:

  • browser caching;
  • повторное использование ресурсов;
  • производительность загрузки.

Manual Chunks

Vite позволяет напрямую управлять Rollup chunk splitting.

Пример:

export default defineConfig({
    build: {
        rollupOptions: {
            output: {
                manualChunks: {
                    vendor: ['vue', 'axios']
                }
            }
        }
    }
})

В этом случае Rollup создаст отдельный chunk:

vendor.[hash].js

Обработка CSS

Rollup участвует и в обработке CSS.

Vite извлекает CSS из модулей:

import './style.css'

После чего Rollup:

  • объединяет стили;
  • удаляет дубликаты;
  • формирует CSS chunks;
  • связывает CSS с JS-чанками.

Обработка ассетов

Rollup умеет работать с:

  • изображениями;
  • шрифтами;
  • SVG;
  • media-файлами.

Пример:

import logo from './logo.png'

Во время production-сборки:

  • файл копируется в dist/assets;
  • генерируется hash;
  • импорт заменяется URL-путем.

Результат:

const logo = "/assets/logo-8d72f.png"

Хеширование файлов

Rollup генерирует content hash:

app-83hf72.js

Это необходимо для:

  • cache busting;
  • долгосрочного кеширования;
  • обновления браузерных ресурсов.

При изменении содержимого hash меняется автоматически.


Минификация

После завершения основной сборки Vite запускает minification.

В современных версиях Vite обычно используются:

  • esbuild;
  • terser.

Rollup подготавливает код к минимизации:

  • объединяет модули;
  • удаляет лишние конструкции;
  • сокращает структуру output.

Формирование output directory

Rollup отвечает за финальную структуру dist.

Пример:

dist/
├── index.html
├── assets/
│   ├── index-A1B2.js
│   ├── vendor-C3D4.js
│   ├── index-E5F6.css
│   └── logo-G7H8.svg

Все связи между файлами автоматически обновляются.


Генерация sourcemaps

Rollup умеет создавать sourcemaps:

build: {
    sourcemap: true
}

В результате:

index.js.map

содержит соответствие между production-кодом и исходными файлами.

Это необходимо для:

  • отладки;
  • анализа ошибок;
  • stack traces;
  • devtools.

Rollup Plugins внутри Vite

Vite поддерживает Rollup plugins через:

build: {
    rollupOptions: {
        plugins: []
    }
}

Пример:

import visualizer from 'rollup-plugin-visualizer'

export default defineConfig({
    build: {
        rollupOptions: {
            plugins: [visualizer()]
        }
    }
})

Плагин может:

  • анализировать чанки;
  • модифицировать output;
  • добавлять трансформации;
  • изменять генерацию файлов.

Output Formats

Rollup поддерживает разные форматы сборки:

  • ESM;
  • CommonJS;
  • UMD;
  • IIFE;
  • SystemJS.

Vite чаще всего использует ESM для web-приложений, однако library mode позволяет генерировать разные форматы.

Пример:

build: {
    lib: {
        entry: 'src/index.js',
        name: 'MyLibrary'
    }
}

Rollup создаст библиотечные bundles.


Library Mode

В режиме библиотеки Rollup становится особенно важным.

Он отвечает за:

  • экспорт API;
  • внешние зависимости;
  • multiple outputs;
  • package compatibility.

Пример external dependencies:

rollupOptions: {
    external: ['vue']
}

Тогда Vue не попадет в bundle.


Оптимизация импортов

Rollup может объединять импорты:

Исходный код:

import { ref } from 'vue'
import { reactive } from 'vue'

После оптимизации:

import { ref, reactive } from 'vue'

Это уменьшает размер output.


Static Analysis

Rollup активно использует static analysis.

Он анализирует:

  • структуру модулей;
  • области видимости;
  • экспортируемые символы;
  • точки входа;
  • re-export цепочки.

Благодаря статическому анализу достигается:

  • эффективный tree shaking;
  • минимизация runtime overhead;
  • компактный output.

Ограничения Rollup

Несмотря на высокую эффективность, Rollup имеет ограничения.

Хуже работает с dynamic patterns

Проблемные конструкции:

require(variable)

или:

globalThis[someName]()

Статический анализ таких выражений затруднен.

Зависимость от ESM

Rollup максимально эффективен именно с ES Modules. Legacy CommonJS-пакеты требуют дополнительной обработки.


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

В production-сборке Vite одновременно использует:

  • Rollup;
  • esbuild.

Разделение задач выглядит так:

Инструмент Основная роль
Rollup Bundling и graph analysis
esbuild Трансформация и minification

Rollup управляет архитектурой сборки, а esbuild обеспечивает высокую скорость отдельных операций.


Почему Rollup особенно эффективен для Vite

Архитектура Vite идеально сочетается с Rollup по нескольким причинам:

  • Vite полностью ориентирован на ESM;
  • dev и production используют единый import syntax;
  • отсутствует необходимость в тяжелом runtime;
  • tree shaking работает максимально эффективно;
  • output получается компактным и предсказуемым.

В результате production-сборки Vite обычно:

  • быстро генерируются;
  • имеют небольшой размер;
  • хорошо кешируются;
  • эффективно загружаются браузером;
  • легко масштабируются на крупные приложения.