Настройка Rollup через build.rollupOptions

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

  • изменять точки входа;
  • управлять структурой выходных файлов;
  • разделять чанки;
  • подключать Rollup-плагины;
  • настраивать внешние зависимости;
  • контролировать формат сборки;
  • влиять на tree shaking;
  • создавать библиотечные сборки;
  • формировать многовходовые проекты.

Базовый пример:

import { defineConfig } from 'vite'

export default defineConfig({
    build: {
        rollupOptions: {
            
        }
    }
})

Структура rollupOptions

Объект rollupOptions поддерживает практически все параметры Rollup:

export default defineConfig({
    build: {
        rollupOptions: {
            input,
            output,
            plugins,
            external,
            treeshake,
            preserveEntrySignatures,
            onwarn
        }
    }
})

Наиболее важными являются:

Параметр Назначение
input Точки входа
output Настройка выходных файлов
plugins Rollup-плагины
external Исключение зависимостей из бандла
treeshake Управление tree shaking
onwarn Перехват предупреждений
manualChunks Ручное разделение чанков

Настройка точек входа через input

Обычная сборка

По умолчанию Vite использует index.html как entry point.

Эквивалент:

rollupOptions: {
    input: 'index.html'
}

Несколько HTML-страниц

Многовходовая архитектура используется в:

  • административных панелях;
  • лендингах;
  • CMS;
  • MPА-приложениях;
  • корпоративных сайтах.

Пример:

import { defineConfig } from 'vite'
import { resolve } from 'path'

export default defineConfig({
    build: {
        rollupOptions: {
            input: {
                main: resolve(__dirname, 'index.html'),
                admin: resolve(__dirname, 'admin.html'),
                dashboard: resolve(__dirname, 'dashboard.html')
            }
        }
    }
})

После сборки каждая HTML-страница получит собственный набор ресурсов.


Вход через JavaScript

Иногда требуется собирать не HTML, а JS-модуль.

Пример:

rollupOptions: {
    input: 'src/main.js'
}

Подобный подход применяется:

  • в библиотеках;
  • SDK;
  • npm-пакетах;
  • виджетах;
  • UI-kit проектах.

Настройка output

Раздел output отвечает за структуру итоговой сборки.

Базовый пример

rollupOptions: {
    output: {
        dir: 'dist',
        format: 'es'
    }
}

Настройка имён файлов

По умолчанию Vite генерирует файлы с hash:

assets/index-a1b2c3.js

Настройка:

rollupOptions: {
    output: {
        entryFileNames: 'js/[name].js',
        chunkFileNames: 'js/[name].js',
        assetFileNames: 'assets/[name].[ext]'
    }
}

Результат:

dist/
├── js/
│   ├── main.js
│   └── vendor.js
└── assets/
    └── logo.svg

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

Для production почти всегда нужен hash.

Пример:

output: {
    entryFileNames: 'js/[name]-[hash].js',
    chunkFileNames: 'js/[name]-[hash].js',
    assetFileNames: 'assets/[name]-[hash].[ext]'
}

Разделение ресурсов по типам

Можно сортировать ресурсы автоматически.

Пример:

output: {
    assetFileNames(assetInfo) {

        const ext = assetInfo.name.split('.').pop()

        if (/png|jpg|svg|gif/.test(ext)) {
            return 'images/[name]-[hash].[ext]'
        }

        if (/css/.test(ext)) {
            return 'css/[name]-[hash].[ext]'
        }

        return 'assets/[name]-[hash].[ext]'
    }
}

Результат:

dist/
├── images/
├── css/
└── assets/

Форматы сборки

Rollup поддерживает различные форматы модулей.

ES Modules

output: {
    format: 'es'
}

Современный стандарт JavaScript-модулей.


CommonJS

output: {
    format: 'cjs'
}

Используется в Node.js.


UMD

output: {
    format: 'umd',
    name: 'MyLibrary'
}

Подходит для подключения через <script>.


IIFE

output: {
    format: 'iife',
    name: 'App'
}

Создаёт самовызывающийся bundle.


Ручное разделение чанков через manualChunks

Одна из важнейших возможностей Rollup.

Назначение

manualChunks позволяет:

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

Простейший пример

output: {
    manualChunks: {
        vendor: ['vue']
    }
}

Будет создан отдельный chunk:

vendor.js

Выделение нескольких библиотек

output: {
    manualChunks: {
        vue: ['vue'],
        charts: ['chart.js'],
        editor: ['quill']
    }
}

Автоматическое разделение node_modules

Очень распространённая практика:

output: {
    manualChunks(id) {

        if (id.includes('node_modules')) {
            return 'vendor'
        }
    }
}

Разделение по пакетам

Более гибкий вариант:

output: {
    manualChunks(id) {

        if (id.includes('node_modules')) {

            return id
                .split('node_modules/')[1]
                .split('/')[0]
        }
    }
}

Результат:

react.js
lodash.js
axios.js

Исключение зависимостей через external

Параметр external запрещает Rollup включать модуль в bundle.

Пример

rollupOptions: {
    external: ['vue']
}

Vue не попадёт в итоговую сборку.


Когда используется

Типичные случаи:

  • библиотечная сборка;
  • CDN-подключение;
  • peerDependencies;
  • Node.js-пакеты.

Работа с глобальными переменными

При UMD/IIFE требуется указать глобальное имя:

output: {
    globals: {
        vue: 'Vue'
    }
}

Полный пример:

rollupOptions: {
    external: ['vue'],
    output: {
        globals: {
            vue: 'Vue'
        }
    }
}

Подключение Rollup-плагинов

Vite поддерживает Rollup-плагины напрямую.

Пример подключения

import legacy from '@vitejs/plugin-legacy'

export default defineConfig({
    plugins: [
        legacy()
    ]
})

Но существуют случаи, когда нужен именно Rollup-plugin:

import strip from '@rollup/plugin-strip'

export default defineConfig({
    build: {
        rollupOptions: {
            plugins: [
                strip({
                    debugger: true
                })
            ]
        }
    }
})

Популярные Rollup-плагины

Плагин Назначение
@rollup/plugin-alias Алиасы
@rollup/plugin-replace Замена значений
@rollup/plugin-strip Удаление debugger
@rollup/plugin-inject Автоимпорт
rollup-plugin-visualizer Анализ bundle
@rollup/plugin-commonjs CommonJS поддержка

Анализ bundle через visualizer

Очень полезный инструмент.

Установка:

npm install rollup-plugin-visualizer --save-dev

Конфигурация:

import { visualizer } from 'rollup-plugin-visualizer'

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

После сборки откроется интерактивная карта bundle.


Tree Shaking

Rollup обладает одним из лучших механизмов tree shaking.

Настройка

rollupOptions: {
    treeshake: true
}

Гибкая конфигурация

rollupOptions: {
    treeshake: {
        moduleSideEffects: false,
        propertyReadSideEffects: false,
        tryCatchDeoptimization: false
    }
}

moduleSideEffects

Указывает, имеет ли модуль побочные эффекты.

treeshake: {
    moduleSideEffects: false
}

Rollup сможет агрессивнее удалять код.


Настройка предупреждений через onwarn

Позволяет перехватывать предупреждения Rollup.

Пример

rollupOptions: {
    onwarn(warning, warn) {

        if (warning.code === 'CIRCULAR_DEPENDENCY') {
            return
        }

        warn(warning)
    }
}

Сохранение структуры модулей

preserveModules

Позволяет сохранять файловую структуру.

output: {
    preserveModules: true
}

Результат

Вместо одного bundle:

dist/
├── utils/
├── components/
└── services/

Когда используется

Типичные сценарии:

  • библиотеки;
  • SSR;
  • package exports;
  • tree-shakable SDK.

Library Mode и Rollup

Vite поддерживает режим библиотечной сборки.

Базовая настройка

export default defineConfig({
    build: {
        lib: {
            entry: 'src/index.js',
            name: 'MyLib',
            fileName: 'my-lib'
        },
        rollupOptions: {
            external: ['vue'],
            output: {
                globals: {
                    vue: 'Vue'
                }
            }
        }
    }
})

Несколько форматов

build: {
    lib: {
        entry: 'src/index.js',
        name: 'MyLib',
        formats: ['es', 'umd', 'cjs']
    }
}

Работа с динамическими импортами

Rollup корректно обрабатывает:

const module = await import('./module.js')

Влияние на чанки

Каждый dynamic import создаёт отдельный chunk.

Пример:

const AdminPanel = () => import('./AdminPanel.js')

После сборки:

admin-panel.js

Inline Dynamic Imports

Позволяет объединять dynamic imports в один файл.

output: {
    inlineDynamicImports: true
}

Ограничения

Нельзя использовать:

  • multi-entry;
  • code splitting;
  • manualChunks.

Контроль sourcemap

Rollup позволяет гибко управлять sourcemap.

output: {
    sourcemap: true
}

Hidden sourcemap

build: {
    sourcemap: 'hidden'
}

Карта будет создана без ссылки внутри JS-файла.


Конфигурация для больших проектов

Пример production-конфигурации:

import { defineConfig } from 'vite'

export default defineConfig({

    build: {

        sourcemap: false,

        rollupOptions: {

            output: {

                entryFileNames: 'js/[name]-[hash].js',

                chunkFileNames: 'js/[name]-[hash].js',

                assetFileNames(assetInfo) {

                    const ext = assetInfo.name
                        .split('.')
                        .pop()

                    if (/css/.test(ext)) {
                        return 'css/[name]-[hash].[ext]'
                    }

                    if (/png|jpg|svg|gif/.test(ext)) {
                        return 'images/[name]-[hash].[ext]'
                    }

                    return 'assets/[name]-[hash].[ext]'
                },

                manualChunks(id) {

                    if (id.includes('node_modules')) {
                        return 'vendor'
                    }
                }
            }
        }
    }
})

Частые ошибки

Конфликт manualChunks

Неправильное разделение может вызвать:

  • дублирование зависимостей;
  • циклические чанки;
  • увеличение bundle size.

Неверный external

Если исключить зависимость ошибочно:

external: ['react']

но не подключить React отдельно, приложение сломается.


Несовместимость плагинов

Не все Rollup-плагины корректно работают с Vite.

Причины:

  • Vite использует собственный pipeline;
  • часть трансформаций выполняется esbuild;
  • dev-server отличается от Rollup build.

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

Для SPA

Подходит:

manualChunks(id) {
    if (id.includes('node_modules')) {
        return 'vendor'
    }
}

Для библиотек

Критически важны:

external
globals
formats
preserveModules

Для крупных приложений

Рекомендуется:

  • разделять vendor chunks;
  • анализировать bundle;
  • использовать hash;
  • выносить медленные библиотеки;
  • минимизировать initial JS.

Для SSR

Полезны:

preserveModules
external

Пример комплексной настройки

import { defineConfig } from 'vite'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({

    build: {

        sourcemap: false,

        rollupOptions: {

            external: ['vue'],

            plugins: [
                visualizer()
            ],

            output: {

                format: 'es',

                globals: {
                    vue: 'Vue'
                },

                entryFileNames: 'js/[name]-[hash].js',

                chunkFileNames: 'js/[name]-[hash].js',

                assetFileNames: 'assets/[name]-[hash].[ext]',

                manualChunks(id) {

                    if (id.includes('node_modules')) {
                        return 'vendor'
                    }
                }
            },

            treeshake: {
                moduleSideEffects: false
            },

            onwarn(warning, warn) {

                if (warning.code === 'CIRCULAR_DEPENDENCY') {
                    return
                }

                warn(warning)
            }
        }
    }
})