В процессе сборки приложения разные окружения требуют различного
поведения кода. Разработка ориентирована на скорость, удобство отладки и
поддержку HMR, тогда как production-сборка требует минимизации размера,
оптимизации производительности и удаления служебной логики. Vite
предоставляет механизм разделения поведения через переменные окружения,
режимы (mode) и условные трансформации.
Трансформации по окружению позволяют:
Vite использует несколько уровней окружений:
| Тип | Назначение |
|---|---|
development |
dev-сервер |
production |
production-сборка |
test |
тестирование |
| пользовательские режимы | staging, qa, preview и др. |
Ключевую роль играет параметр mode.
Пример запуска:
vite --mode staging
или:
vite build --mode production
Текущее окружение доступно через:
import.meta.env.MODE
Пример:
console.log(import.meta.env.MODE)
Vite внедряет набор специальных переменных:
| Переменная | Назначение |
|---|---|
DEV |
режим разработки |
PROD |
production |
MODE |
имя режима |
BASE_URL |
базовый URL |
SSR |
SSR-режим |
Пример:
if (import.meta.env.DEV) {
console.log('Режим разработки')
}
Во время production-сборки такие условия статически анализируются и оптимизируются.
Одно из главных преимуществ Vite — возможность удаления неиспользуемого кода на этапе сборки.
Пример:
if (import.meta.env.PROD) {
enableAnalytics()
}
Во время development:
if (false) {
enableAnalytics()
}
Rollup и esbuild удаляют мёртвые ветки.
Аналогично:
if (import.meta.env.DEV) {
console.log('debug')
}
В production этот код полностью исчезает.
Это называется:
Во время работы dev-сервера Vite:
Трансформации выполняются динамически.
Во время vite build:
Трансформации становятся статическими.
Конфигурация может зависеть от окружения.
Пример:
import { defineConfig } from 'vite'
export default defineConfig(({ command, mode }) => {
const isDev = command === 'serve'
const isProd = command === 'build'
return {
build: {
sourcemap: isDev
}
}
})
Параметры:
| Параметр | Значение |
|---|---|
command |
serve или build |
mode |
текущее окружение |
Часто окружения требуют полностью разных настроек.
Пример:
export default defineConfig(({ mode }) => {
if (mode === 'development') {
return {
server: {
port: 3000
}
}
}
if (mode === 'production') {
return {
build: {
minify: 'esbuild'
}
}
}
})
Vite автоматически загружает:
| Файл | Назначение |
|---|---|
.env |
общие переменные |
.env.local |
локальные |
.env.development |
development |
.env.production |
production |
Пример:
VITE_API_URL=https://api.dev.local
Production:
VITE_API_URL=https://api.example.com
Использование:
fetch(import.meta.env.VITE_API_URL)
В браузер попадают только переменные с префиксом
VITE_.
Допустимо:
VITE_API_URL=https://example.com
Недопустимо:
SECRET_KEY=123
SECRET_KEY останется доступной только внутри
Node.js.
Это предотвращает случайную утечку секретов.
SSR требует отдельной логики.
Пример:
if (import.meta.env.SSR) {
loadFromDatabase()
}
Клиентская версия:
if (!import.meta.env.SSR) {
initBrowserAPI()
}
Во время SSR-сборки Vite заменяет флаги статически.
Некоторые API отсутствуют на сервере:
windowdocumentlocalStorageНеправильный код:
localStorage.setItem('theme', 'dark')
Без проверки SSR приложение завершится ошибкой.
Правильный вариант:
if (!import.meta.env.SSR) {
localStorage.setItem('theme', 'dark')
}
Трансформации позволяют исключать модули из bundle.
Пример:
if (import.meta.env.DEV) {
import('./debug.js')
}
Production-сборка может исключить debug.js.
Vite поддерживает внедрение compile-time значений.
Пример:
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify('1.0.0')
}
})
Использование:
console.log(__APP_VERSION__)
Во время сборки происходит простая текстовая подстановка.
Пример:
export default defineConfig(({ mode }) => ({
define: {
__DEVTOOLS__: mode === 'development'
}
}))
Использование:
if (__DEVTOOLS__) {
startDevtools()
}
В production ветка удалится.
Плагины могут включаться только в нужных режимах.
Пример:
import legacy from '@vitejs/plugin-legacy'
export default defineConfig(({ mode }) => ({
plugins: [
mode === 'production' && legacy()
]
}))
Обычно используется:
plugins: [
isProd && legacy()
].filter(Boolean)
Это удаляет:
falsenullundefinedиз массива плагинов.
Можно подменять реализации модулей.
Development:
resolve: {
alias: {
'@api': '/src/api/mock.js'
}
}
Production:
resolve: {
alias: {
'@api': '/src/api/real.js'
}
}
Dev-сервер часто использует proxy.
Пример:
server: {
proxy: {
'/api': {
target: 'http://localhost:5000'
}
}
}
В production proxy обычно отсутствует, поскольку запросы идут через CDN, nginx или backend.
Пример:
build: {
minify: mode === 'production'
}
Допустимые варианты:
| Значение | Описание |
|---|---|
false |
без минификации |
esbuild |
быстрая |
terser |
более глубокая |
Development требует sourcemap для отладки.
Production часто отключает их.
Пример:
build: {
sourcemap: mode !== 'production'
}
Иногда production sourcemap сохраняются только для Sentry.
Production может использовать агрессивный code splitting.
Пример:
build: {
rollupOptions: {
output: {
manualChunks: mode === 'production'
? {
vendor: ['vue']
}
: undefined
}
}
}
Development может использовать современные API браузера.
Production иногда требует совместимости.
Пример:
build: {
target: mode === 'production'
? 'es2018'
: 'esnext'
}
Плагины получают информацию об окружении.
Пример:
export default function myPlugin() {
return {
name: 'my-plugin',
transform(code, id) {
if (process.env.NODE_ENV === 'production') {
return code.replace('__DEBUG__', 'false')
}
return code
}
}
}
Плагин может получить финальную конфигурацию.
Пример:
configResolved(config) {
console.log(config.mode)
}
Это позволяет адаптировать трансформации.
HTML также может трансформироваться по окружению.
Пример:
transformIndexHtml(html) {
if (process.env.NODE_ENV === 'production') {
return html.replace(
'</head>',
'<script src="/analytics.js"></script></head>'
)
}
return html
}
Production-сборка часто удаляет console-вызовы.
Пример:
esbuild: {
drop: ['console', 'debugger']
}
Иногда:
drop: mode === 'production'
? ['console']
: []
Development использует pre-bundling.
Production — нет.
Пример:
optimizeDeps: {
include: mode === 'development'
? ['lodash']
: []
}
HMR существует только в dev.
Пример:
if (import.meta.hot) {
import.meta.hot.accept(() => {
console.log('Обновление модуля')
})
}
Production-код HMR не содержит.
Пример:
if (import.meta.env.PROD) {
import('./analytics.js').then(module => {
module.init()
})
}
Development не загружает analytics-код.
Development:
if (import.meta.env.DEV) {
enableMockAPI()
}
Production:
if (import.meta.env.PROD) {
disableMockAPI()
}
В Vite предпочтительно использовать:
import.meta.env.MODE
а не:
process.env.NODE_ENV
Причины:
process.env не всегда доступен;mode поддерживает пользовательские режимы;Допустимы собственные режимы:
vite build --mode staging
Файл:
.env.staging
Использование:
if (import.meta.env.MODE === 'staging') {
enablePreviewBanner()
}
Это разные сущности.
Показывает тип запуска:
| Значение | Описание |
|---|---|
serve |
dev-сервер |
build |
production build |
Показывает логическое окружение:
| Значение | Описание |
|---|---|
development |
разработка |
production |
production |
staging |
staging |
qa |
тестирование |
Пример:
vite build --mode development
В этом случае:
| Поле | Значение |
|---|---|
command |
build |
mode |
development |
Неправильно:
console.log(process.env.API_URL)
Правильно:
console.log(import.meta.env.VITE_API_URL)
Неправильно:
API_URL=https://example.com
Правильно:
VITE_API_URL=https://example.com
Неправильно:
import.meta.env.DEV = false
Это compile-time значения.
Неправильно:
fs.readFileSync()
в клиентском коде.
Необходимо разделять окружения.
Крупные проекты часто используют:
| Подход | Назначение |
|---|---|
| feature flags | включение функций |
| mock layers | тестовые API |
| environment adapters | адаптеры окружений |
| runtime config | серверная конфигурация |
| build profiles | разные сборки |
Пример:
define: {
__NEW_UI__: true
}
Использование:
if (__NEW_UI__) {
renderNewUI()
}
Production может содержать полностью другой интерфейс.
Важно различать:
| Тип | Когда применяется |
|---|---|
| build-time | во время сборки |
| runtime | во время выполнения |
import.meta.env относится к build-time.
Backend API-конфигурация часто относится к runtime.