Переменные окружения в Vite используются для управления конфигурацией приложения в зависимости от среды выполнения. Они позволяют:
Особенность Vite заключается в том, что переменные окружения доступны
не только внутри клиентского кода, но и непосредственно внутри
конфигурационного файла vite.config.js или
vite.config.ts.
Vite загружает переменные окружения из нескольких источников:
.env файлов;Поддерживаются следующие файлы:
.env
.env.local
.env.development
.env.production
.env.staging
.env.development.local
.env.production.local
Приоритет загрузки:
process.env
↓
.env
↓
.env.local
↓
.env.[mode]
↓
.env.[mode].local
Чем конкретнее файл, тем выше его приоритет.
Для загрузки переменных окружения внутри Vite-конфига используется
функция loadEnv.
Пример:
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
server: {
port: Number(env.VITE_PORT)
}
}
})
Функция имеет следующий вид:
loadEnv(mode, envDir, prefixes)
| Аргумент | Назначение |
|---|---|
mode |
текущий режим |
envDir |
директория с .env файлами |
prefixes |
фильтрация переменных по префиксам |
Vite передаёт объект контекста в defineConfig.
Пример:
export default defineConfig(({ mode }) => {
console.log(mode)
return {}
})
Возможные значения:
development
production
staging
test
Если приложение запускается так:
vite --mode staging
Vite загрузит:
.env
.env.local
.env.staging
.env.staging.local
Это позволяет формировать полностью отдельные конфигурации окружений.
.envVITE_API_URL=http://localhost:3000
VITE_PORT=5173
.env.productionVITE_API_URL=https://api.example.com
VITE_PORT=8080
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
server: {
host: env.VITE_HOST,
port: Number(env.VITE_PORT),
open: env.VITE_OPEN === 'true'
}
}
})
Даже если значение выглядит как число или boolean, Vite загружает его как строку.
Пример:
VITE_PORT=3000
VITE_HTTPS=true
Фактически:
typeof env.VITE_PORT // string
typeof env.VITE_HTTPS // string
Поэтому требуется ручное преобразование типов.
const port = Number(env.VITE_PORT)
const isHttps = env.VITE_HTTPS === 'true'
VITE_FEATURES=["auth","chat","admin"]
const features = JSON.parse(env.VITE_FEATURES)
В Vite предпочтительнее использовать loadEnv, а не
прямой доступ к process.env.
Проблема:
process.env.VITE_API_URL
может работать нестабильно в зависимости от окружения и платформы.
Правильный подход:
const env = loadEnv(mode, process.cwd())
По умолчанию Vite ищет .env файлы в корне проекта.
Однако директорию можно изменить.
Структура:
project/
config/
env/
.env
.env.production
Конфиг:
import { defineConfig, loadEnv } from 'vite'
import path from 'path'
export default defineConfig(({ mode }) => {
const envDir = path.resolve(__dirname, 'config/env')
const env = loadEnv(mode, envDir)
return {
envDir
}
})
По умолчанию Vite экспортирует только переменные с префиксом
VITE_.
Пример:
VITE_API_URL=https://example.com
SECRET_KEY=123
В клиентском коде будет доступно только:
import.meta.env.VITE_API_URL
Можно загрузить все переменные:
const env = loadEnv(mode, process.cwd(), '')
Теперь будут доступны:
env.SECRET_KEY
Использование пустого префикса:
loadEnv(mode, process.cwd(), '')
может случайно раскрыть чувствительные данные.
Например:
DB_PASSWORD=secret
JWT_SECRET=123456
Если такие значения попадут в клиентский bundle, это приведёт к утечке.
Распространённая практика:
VITE_PUBLIC_API=https://example.com
SERVER_DB_PASSWORD=secret
SERVER_JWT_KEY=123
Клиент получает только:
VITE_PUBLIC_API
Секреты остаются внутри Node.js окружения.
Переменные окружения часто применяются при compile-time замене.
Пример:
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
define: {
__APP_VERSION__: JSON.stringify(env.VITE_APP_VERSION),
__ENABLE_DEBUG__: env.VITE_DEBUG === 'true'
}
}
})
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
server: {
proxy: {
'/api': {
target: env.VITE_API_PROXY,
changeOrigin: true
}
}
}
}
})
export default defineConfig(({ mode }) => {
const isProd = mode === 'production'
return {
build: {
sourcemap: !isProd,
minify: isProd
}
}
})
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig(({ mode }) => {
const isProd = mode === 'production'
return {
plugins: [
vue(),
isProd &&
legacy({
targets: ['defaults']
})
]
}
})
Иногда используют:
plugins: [
vue(),
isProd && legacy()
].filter(Boolean)
Это удаляет:
false
null
undefined
из массива плагинов.
В браузере переменные доступны через:
import.meta.env
Пример:
console.log(import.meta.env.VITE_API_URL)
| loadEnv | import.meta.env |
|---|---|
| используется в Node.js | используется в браузере |
| доступен в vite.config | доступен в клиентском коде |
| требует вызова | встроен автоматически |
Vite автоматически предоставляет ряд служебных переменных.
import.meta.env.MODE
import.meta.env.DEV
import.meta.env.PROD
import.meta.env.BASE_URL
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
build: {
outDir: env.VITE_OUTPUT_DIR,
sourcemap: env.VITE_SOURCEMAP === 'true'
}
}
})
VITE_OUTPUT_DIR=dist-admin
build: {
outDir: env.VITE_OUTPUT_DIR
}
Это особенно полезно для multi-app проектов.
VITE_BASE=/admin/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
base: env.VITE_BASE
}
})
В monorepo .env файлы могут храниться отдельно.
Пример:
root/
apps/
admin/
client/
env/
.env
Конфиг:
loadEnv(mode, '../. ./env')
Иногда вместо loadEnv используют
dotenv.
Пример:
import dotenv from 'dotenv'
dotenv.config()
console.log(process.env.API_URL)
Однако это нарушает стандартный механизм Vite:
Файл:
src/vite-env.d.ts
Пример:
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_PORT: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
const port = Number(env.VITE_PORT)
Либо:
const port: number = parseInt(env.VITE_PORT, 10)
if (!env.VITE_API_URL) {
throw new Error('VITE_API_URL is required')
}
function validateEnv(env) {
const required = [
'VITE_API_URL',
'VITE_PORT'
]
for (const key of required) {
if (!env[key]) {
throw new Error(`Missing env: ${key}`)
}
}
}
npm install zod
import { z } from 'zod'
const EnvSchema = z.object({
VITE_API_URL: z.string().url(),
VITE_PORT: z.string()
})
const parsed = EnvSchema.parse(env)
После изменения .env файлов dev server часто требует
перезапуска.
Причина:
Частая проблема:
import.meta.env.API_URL
Возвращает:
undefined
Причина:
нет префикса VITE_
Правильно:
VITE_API_URL=https://example.com
Префикс можно изменить.
export default defineConfig({
envPrefix: 'APP_'
})
Теперь доступны:
APP_API_URL=https://example.com
envPrefix: ['VITE_', 'APP_']
VITE_API_URL=http://localhost:3000
VITE_DEBUG=true
VITE_API_URL=https://staging-api.example.com
VITE_DEBUG=true
VITE_API_URL=https://api.example.com
VITE_DEBUG=false
Обычно:
.env.local
добавляют в:
.gitignore
Особенно если файл содержит:
Пример GitHub Actions:
env:
VITE_API_URL: https://api.example.com
Vite автоматически прочитает эти значения через
process.env.
Иногда полезно вывести env:
console.log(loadEnv(mode, process.cwd(), ''))
Это помогает:
На Windows переменные окружения в shell отличаются.
Пример:
set NODE_ENV=production
PowerShell:
$env:NODE_ENV="production"
Linux/macOS:
NODE_ENV=production
Для кроссплатформенности часто используют:
cross-env NODE_ENV=production vite build
Условные флаги позволяют удалять код на этапе сборки.
Пример:
if (import.meta.env.PROD) {
enableAnalytics()
}
Во время production build ненужные ветки могут быть исключены Rollup и esbuild.
VITE_ENABLE_CHAT=true
VITE_ENABLE_ADMIN=false
if (import.meta.env.VITE_ENABLE_CHAT === 'true') {
initChat()
}
Нельзя хранить в клиентских env:
Все значения с клиентским префиксом фактически становятся публичными после сборки.
Часто создают отдельный модуль:
src/config/env.ts
Пример:
export const ENV = {
apiUrl: import.meta.env.VITE_API_URL,
debug: import.meta.env.VITE_DEBUG === 'true'
}
Это создаёт: