В крупных проектах на базе Vite нередко возникает необходимость изменять поведение сборки в зависимости от текущего окружения. Разработка, тестирование, production-сборка, SSR, preview-режим, staging — каждое окружение может требовать собственных плагинов, отдельных настроек или различной конфигурации уже существующих расширений.
Механизм условного подключения плагинов позволяет:
Конфигурация vite.config.js или
vite.config.ts может экспортировать не объект, а функцию.
Эта функция получает информацию о текущем режиме запуска.
Пример:
import { defineConfig } from 'vite'
export default defineConfig(({ command, mode }) => {
console.log(command)
console.log(mode)
return {
plugins: []
}
})
Параметры:
| Параметр | Описание |
|---|---|
command |
Тип запуска: serve или build |
mode |
Активный режим окружения |
isSsrBuild |
Флаг SSR-сборки |
isPreview |
Режим preview |
Многие разработчики смешивают эти понятия, хотя они решают разные задачи.
Определяет сам сценарий запуска:
vite
vite build
Возможные значения:
command === 'serve'
command === 'build'
Использование:
plugins: command === 'serve'
? [devPlugin()]
: [prodPlugin()]
Mode — логическое окружение приложения.
Примеры:
vite --mode development
vite build --mode production
vite build --mode staging
Использование:
if (mode === 'production') {
// production-конфигурация
}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import checker from 'vite-plugin-checker'
export default defineConfig(({ command }) => {
return {
plugins: [
vue(),
command === 'serve'
? checker({
typescript: true
})
: null
]
}
})
При условном подключении массив плагинов часто содержит
null.
Стандартный подход:
plugins: [
isDev ? devPlugin() : null
].filter(Boolean)
Полный пример:
export default defineConfig(({ command }) => {
const isDev = command === 'serve'
return {
plugins: [
vue(),
isDev
? devOnlyPlugin()
: null
].filter(Boolean)
}
})
export default defineConfig(({ mode }) => {
return {
plugins: [
mode === 'production'
? compressionPlugin()
: null
].filter(Boolean)
}
})
export default defineConfig(({ mode }) => {
return {
plugins: [
mode === 'staging'
? debugPlugin()
: null
].filter(Boolean)
}
})
Плагины могут подключаться через .env-переменные.
VITE_ENABLE_ANALYZER=true
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
plugins: [
env.VITE_ENABLE_ANALYZER === 'true'
? analyzerPlugin()
: null
].filter(Boolean)
}
})
Production-режим обычно содержит:
import compression from 'vite-plugin-compression'
export default defineConfig(({ mode }) => {
const isProd = mode === 'production'
return {
plugins: [
isProd
? compression({
algorithm: 'gzip'
})
: null
].filter(Boolean)
}
})
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig(({ mode }) => {
return {
plugins: [
mode === 'production'
? visualizer({
open: true
})
: null
].filter(Boolean)
}
})
Во время разработки активно используются:
import { viteMockServe } from 'vite-plugin-mock'
export default defineConfig(({ command }) => {
const isDev = command === 'serve'
return {
plugins: [
isDev
? viteMockServe({
mockPath: 'mock'
})
: null
].filter(Boolean)
}
})
SSR требует отдельной логики подключения.
export default defineConfig(({ isSsrBuild }) => {
return {
plugins: [
isSsrBuild
? serverPlugin()
: clientPlugin()
]
}
})
plugins: [
isSsrBuild
? ssrTransformPlugin()
: browserTransformPlugin()
]
Команда:
vite build --mode staging
Файл:
.env.staging
Конфигурация:
export default defineConfig(({ mode }) => {
const isStaging = mode === 'staging'
return {
plugins: [
isStaging
? stagingPlugin()
: null
].filter(Boolean)
}
})
В больших проектах конфигурация быстро разрастается.
function createDevPlugins() {
return [
mockPlugin(),
checkerPlugin()
]
}
function createProdPlugins() {
return [
compressionPlugin(),
analyzerPlugin()
]
}
export default defineConfig(({ mode }) => {
const isProd = mode === 'production'
return {
plugins: [
vue(),
...(isProd
? createProdPlugins()
: createDevPlugins())
]
}
})
Иногда требуется избежать импорта тяжёлых зависимостей в development.
export default defineConfig(async ({ mode }) => {
const plugins = []
if (mode === 'production') {
const { visualizer } =
await import('rollup-plugin-visualizer')
plugins.push(
visualizer()
)
}
return {
plugins
}
})
Конфигурация Vite поддерживает async-функции.
export default defineConfig(async () => {
const data = await loadRemoteConfig()
return {
plugins: [
createPlugin(data)
]
}
})
Необязательно подключать разные плагины. Часто достаточно менять параметры.
checker({
overlay: mode === 'development',
terminal: true
})
debugPlugin({
verbose: mode !== 'production'
})
Наиболее гибкий вариант.
export default defineConfig(({ command, mode }) => {
const isDevServer =
command === 'serve'
const isProduction =
mode === 'production'
return {
plugins: [
isDevServer && devPlugin(),
isProduction && compressionPlugin()
].filter(Boolean)
}
})
vite preview запускает production-сборку локально.
export default defineConfig(({ isPreview }) => {
return {
plugins: [
isPreview
? previewPlugin()
: null
].filter(Boolean)
}
})
Иногда mode недостаточно.
VITE_USE_LEGACY=true
const useLegacy =
env.VITE_USE_LEGACY === 'true'
plugins: [
useLegacy
? legacyPlugin()
: null
].filter(Boolean)
Крупные проекты обычно используют фабрики.
export function createPlugins(options) {
const plugins = []
if (options.dev) {
plugins.push(devPlugin())
}
if (options.prod) {
plugins.push(prodPlugin())
}
return plugins
}
export default defineConfig(({ mode, command }) => {
return {
plugins: createPlugins({
dev: command === 'serve',
prod: mode === 'production'
})
}
})
interface PluginOptions {
dev: boolean
prod: boolean
ssr: boolean
}
function createPlugins(
options: PluginOptions
) {
return []
}
Ошибка:
plugins: [
isProd ? plugin() : null
]
Некоторые плагины или инструменты анализа могут некорректно
обрабатывать null.
Правильно:
plugins: [
isProd ? plugin() : null
].filter(Boolean)
mode и process.env.NODE_ENV — не одно и то
же.
mode === 'production'
не всегда эквивалентно:
process.env.NODE_ENV === 'production'
В экосистеме Vite рекомендуется использовать именно
mode.
Плохо:
import { visualizer }
from 'rollup-plugin-visualizer'
Даже если плагин не используется, модуль всё равно загружается.
Лучше:
const { visualizer } =
await import('rollup-plugin-visualizer')
Типичная структура:
config/
├─ plugins/
│ ├─ dev.ts
│ ├─ prod.ts
│ ├─ ssr.ts
│ └─ index.ts
├─ env/
├─ build/
└─ vite/
import { createDevPlugins }
from './dev'
import { createProdPlugins }
from './prod'
export function createPlugins(ctx) {
return [
...createDevPlugins(ctx),
...createProdPlugins(ctx)
]
}
Так как Vite использует Rollup внутри production-сборки, правила распространяются и на Rollup-плагины.
build: {
rollupOptions: {
plugins: [
isProd
? rollupPlugin()
: null
].filter(Boolean)
}
}
Плагины могут менять поведение в зависимости от окружения.
function customPlugin(isProd) {
return {
name: 'custom-plugin',
transform(code) {
if (isProd) {
return optimize(code)
}
return injectDebug(code)
}
}
}
В monorepo часто используются разные окружения для разных пакетов.
const packageMode =
process.env.PACKAGE_TARGET
plugins: [
packageMode === 'admin'
? adminPlugin()
: sitePlugin()
]
vite.config.ts
Содержит всю логику.
Подходит для небольших проектов.
plugins/
builders/
env/
ssr/
Используется в enterprise-проектах.
Главный конфиг содержит только orchestration-логику:
plugins: createPlugins(context)
Вся остальная логика распределяется по модулям.
import { defineConfig, loadEnv }
from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig(async ({
mode,
command,
isSsrBuild
}) => {
const env =
loadEnv(mode, process.cwd())
const plugins = [vue()]
if (command === 'serve') {
const { default: checker }
= await import(
'vite-plugin-checker'
)
plugins.push(
checker({
typescript: true
})
)
}
if (mode === 'production') {
const { visualizer }
= await import(
'rollup-plugin-visualizer'
)
plugins.push(
visualizer()
)
}
if (isSsrBuild) {
plugins.push(
ssrPlugin()
)
}
if (
env.VITE_ENABLE_GZIP === 'true'
) {
const compression =
await import(
'vite-plugin-compression'
)
plugins.push(
compression.default()
)
}
return {
plugins
}
})