В процессе разработки приложения окружение постоянно меняется. Локальная разработка, тестовый сервер и production используют разные API, базы данных, режимы логирования, флаги оптимизации и ключи доступа. Хранение всех переменных в одном файле быстро приводит к ошибкам и путанице.
Webpack позволяет организовать разделение окружений через несколько
.env-файлов:
.env
.env.development
.env.production
.env.local
.env.test
Наиболее распространённая схема:
.env.development
.env.production
Такой подход позволяет автоматически подставлять нужные значения в зависимости от режима сборки.
.envОдин общий файл окружения создаёт множество рисков:
Пример проблемного файла:
API_URL=https://api.production.com
DEBUG=true
ENABLE_DEVTOOLS=true
При запуске локальной сборки приложение уже работает с production API.
.env.developmentИспользуется во время локальной разработки.
NODE_ENV=development
API_URL=http://localhost:3000/api
DEBUG=true
ENABLE_DEVTOOLS=true
ENABLE_LOGS=true
Особенности:
.env.productionИспользуется при production-сборке.
NODE_ENV=production
API_URL=https://api.site.com
DEBUG=false
ENABLE_DEVTOOLS=false
ENABLE_LOGS=false
Особенности:
Для загрузки env-переменных часто используется плагин
dotenv-webpack.
Установка:
npm install dotenv-webpack --save-dev
Либо:
yarn add dotenv-webpack -D
const Dotenv = require('dotenv-webpack');
module.exports = {
plugins: [
new Dotenv()
]
};
По умолчанию плагин ищет файл:
.env
После подключения плагина переменные доступны через
process.env.
console.log(process.env.API_URL);
Webpack заменяет значения ещё на этапе сборки.
Результат:
console.log("https://api.site.com");
Это важно понимать: в браузере объекта process.env не
существует. Webpack делает compile-time подстановку.
Наиболее правильный подход — динамически выбирать env-файл в зависимости от режима сборки.
const Dotenv = require('dotenv-webpack');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
mode: isProduction ? 'production' : 'development',
plugins: [
new Dotenv({
path: isProduction
? './.env.production'
: './.env.development'
})
]
};
};
npx webpack --mode development
Webpack загрузит:
.env.development
npx webpack --mode production
Webpack загрузит:
.env.production
Крупные проекты часто используют отдельные конфиги:
webpack.common.js
webpack.dev.js
webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const Dotenv = require('dotenv-webpack');
module.exports = merge(common, {
mode: 'development',
plugins: [
new Dotenv({
path: './.env.development'
})
]
});
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const Dotenv = require('dotenv-webpack');
module.exports = merge(common, {
mode: 'production',
plugins: [
new Dotenv({
path: './.env.production'
})
]
});
{
"scripts": {
"dev": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
}
.envИногда создаётся общий файл:
.env
Он содержит значения для всех окружений.
APP_NAME=My Application
APP_VERSION=1.0.0
.env.production может переопределять общий
.env.
API_URL=http://localhost
API_URL=https://api.production.com
Production-файл заменит базовое значение.
Иногда требуется комбинирование:
new Dotenv({
path: './.env.production'
})
Но dotenv-webpack не умеет автоматически объединять
множество env-файлов как это делает Vite или Create React App.
Для сложных схем используется библиотека dotenv.
npm install dotenv --save-dev
const dotenv = require('dotenv');
dotenv.config({
path: './.env.production'
});
module.exports = {
mode: 'production'
};
Часто используется вместе с DefinePlugin.
const webpack = require('webpack');
const dotenv = require('dotenv');
const env = dotenv.config({
path: './.env.production'
}).parsed;
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify(env.API_URL),
'process.env.DEBUG': JSON.stringify(env.DEBUG)
})
]
};
DefinePlugin выполняет текстовую замену.
Неправильно:
'process.env.API_URL': env.API_URL
Результат:
process.env.API_URL = https://api.com
Это синтаксическая ошибка.
Правильно:
JSON.stringify(env.API_URL)
Результат:
"https://api.com"
Webpack активно использует:
process.env.NODE_ENV
Многие библиотеки меняют поведение автоматически.
Пример:
if (process.env.NODE_ENV === 'development') {
console.log('debug');
}
В production Webpack способен удалить этот код через dead code elimination.
Production env-файлы часто управляют:
ENABLE_EXPERIMENTAL_UI=true
ENABLE_EXPERIMENTAL_UI=false
if (process.env.ENABLE_EXPERIMENTAL_UI === 'true') {
renderExperimentalUI();
}
Webpack встраивает env-переменные в итоговый bundle.
Это означает:
SECRET_KEY=123456
после сборки окажется внутри JS-файла.
Нельзя хранить в frontend:
Допустимо:
API_URL=https://api.site.com
SENTRY_DSN=...
APP_VERSION=1.0.0
Недопустимо:
DB_PASSWORD=...
JWT_SECRET=...
AWS_SECRET_ACCESS_KEY=...
Часто используется файл:
.env.local
Он не коммитится в Git.
.env.local
.env.*.local
Файл содержит:
Обычно применяется следующая схема:
.env.env.development.env.local.env.development.localЧем файл специфичнее — тем выше приоритет.
Webpack позволяет передавать переменные напрямую.
API_URL=https://dev.api webpack
set API_URL=https://dev.api && webpack
Для кроссплатформенности применяется cross-env.
npm install cross-env --save-dev
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack"
}
}
const isDev = process.env.NODE_ENV === 'development';
if (isDev) {
enableDevTools();
}
Webpack позволяет исключать модули из production.
if (process.env.NODE_ENV === 'development') {
require('./debug-panel');
}
В production-сборке модуль может быть полностью удалён.
module.exports = {
devtool: 'eval-source-map'
};
module.exports = {
devtool: false
};
Либо:
devtool: 'source-map'
если требуется production debugging.
console.log(process.env.NODE_ENV);
console.log(process.env.API_URL);
Важно различать два типа env:
Подставляются Webpack во время сборки.
process.env.API_URL
Получаются во время работы приложения.
Например:
window.__CONFIG__
или запросом к серверу.
После production-сборки изменить значения нельзя без нового build.
Если bundle содержит:
"https://api.site.com"
то переключить API уже невозможно.
Иногда используется отдельный конфигурационный файл.
{
"apiUrl": "https://api.site.com"
}
Приложение загружает его динамически.
Это позволяет:
Webpack имеет встроенный плагин:
webpack.EnvironmentPlugin
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.EnvironmentPlugin([
'NODE_ENV',
'API_URL'
])
]
};
Плагин читает переменные из текущего окружения Node.js.
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
DEBUG: false
})
.env файлы;process.env;.env;Production env-переменные часто задаются через:
env:
NODE_ENV: production
API_URL: https://api.production.com
ENV NODE_ENV=production
environment:
NODE_ENV: production
API_URL: https://api.site.com
После сборки можно увидеть:
"https://api.site.com"
внутри итогового JS.
Это нормальное поведение compile-time env.
Причины:
Причина:
Webpack не заменил env-переменные.
Обычно:
DefinePlugin;dotenv-webpack.После изменения .env требуется перезапуск:
npm run dev
Webpack Dev Server не всегда отслеживает env-файлы автоматически.
Неправильно:
path: '.env.production'
при запуске из другой директории.
Надёжнее:
const path = require('path');
path: path.resolve(__dirname, '.env.production')
project/
│
├── src/
├── dist/
├── .env
├── .env.development
├── .env.production
├── .env.local
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
└── package.json