NODE_ENV — одна из ключевых переменных окружения в
экосистеме JavaScript и Node.js. В контексте Webpack она используется
для переключения поведения сборки между различными режимами работы
приложения.
Наиболее распространённые значения:
NODE_ENV=development
NODE_ENV=production
NODE_ENV=test
Webpack, плагины, загрузчики, Babel, React, Vue и множество других инструментов ориентируются на значение этой переменной для изменения логики работы.
Переменные окружения представляют собой внешние параметры процесса, передаваемые операционной системой.
Пример:
NODE_ENV=production webpack
Во время запуска процесса Node.js значение становится доступным через объект:
process.env.NODE_ENV
Пример:
console.log(process.env.NODE_ENV);
Результат:
production
NODE_ENVГлавная задача NODE_ENV — разделение среды выполнения
приложения.
Обычно используются два режима:
| Режим | Назначение |
|---|---|
| development | разработка |
| production | production-сборка |
| test | тестирование |
Разные режимы требуют различного поведения:
Для разработки важны:
Для production-сборки важны:
NODE_ENV и
mode в WebpackWebpack имеет собственную настройку:
module.exports = {
mode: 'production'
};
или:
module.exports = {
mode: 'development'
};
Важно понимать различие:
| Параметр | Назначение |
|---|---|
mode |
режим работы Webpack |
NODE_ENV |
глобальная переменная окружения |
modeWebpack автоматически включает набор внутренних оптимизаций.
mode: developmentАвтоматически активируются:
mode: productionАвтоматически включаются:
process.env.NODE_ENVWebpack автоматически подставляет значение
process.env.NODE_ENV, если используется
mode.
Пример:
module.exports = {
mode: 'production'
};
Webpack фактически выполняет замену:
process.env.NODE_ENV
на:
'production'
Webpack не использует настоящие переменные окружения внутри браузерного JavaScript.
Во время сборки происходит статическая замена кода.
Исходный код:
if (process.env.NODE_ENV === 'development') {
console.log('debug');
}
После production-сборки:
if ('production' === 'development') {
console.log('debug');
}
Минификатор видит невозможное условие и удаляет код полностью.
Одна из важнейших причин использования NODE_ENV —
исключение отладочной логики из production.
Пример:
if (process.env.NODE_ENV !== 'production') {
console.log('Application started');
}
В production:
if ('production' !== 'production') {
console.log('Application started');
}
После минификации:
// код удалён
Использование NODE_ENV=production значительно уменьшает
размер итоговой сборки.
Причины:
Особенно сильно это влияет на React.
NODE_ENV и ReactReact содержит две версии внутренней логики:
Пример:
if (process.env.NODE_ENV !== 'production') {
warning(...);
}
В development React включает:
В production всё это удаляется.
Разница в размере может достигать сотен килобайт.
Для передачи NODE_ENV используется
DefinePlugin.
Пример:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
JSON.stringifyНеправильный вариант:
'process.env.NODE_ENV': 'production'
Webpack подставит:
production
без кавычек.
Это приведёт к ошибке:
if (production === 'development')
Правильный вариант:
'process.env.NODE_ENV': JSON.stringify('production')
Результат:
if ('production' === 'development')
Webpack предоставляет сокращённую форму:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.EnvironmentPlugin([
'NODE_ENV'
])
]
};
Полностью вручную определяет значения:
new webpack.DefinePlugin({
VERSION: JSON.stringify('1.0')
})
Берёт значения из реального окружения Node.js:
NODE_ENV=production webpack
и автоматически передаёт их в сборку.
NODE_ENV через npm scriptsПример:
{
"scripts": {
"dev": "NODE_ENV=development webpack serve",
"build": "NODE_ENV=production webpack"
}
}
На Windows такой синтаксис не работает:
NODE_ENV=production webpack
Используется пакет cross-env.
Установка:
npm install cross-env --save-dev
Пример:
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack"
}
}
NODE_ENV внутри webpack.config.jsПеременная доступна во время выполнения конфигурации.
Пример:
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
mode: isProd ? 'production' : 'development'
};
module.exports = {
devtool: isProd
? false
: 'source-map'
};
optimization: {
minimize: isProd
}
output: {
filename: isProd
? '[name].[contenthash].js'
: '[name].js'
}
plugins: [
!isProd && new webpack.HotModuleReplacementPlugin()
].filter(Boolean)
Часто используется единый файл:
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
mode: isProd ? 'production' : 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProd
? '[name].[contenthash].js'
: '[name].js'
},
devtool: isProd
? false
: 'eval-source-map'
};
Другой популярный подход:
webpack.common.js
webpack.dev.js
webpack.prod.js
module.exports = {
mode: 'development',
devtool: 'eval-source-map'
};
module.exports = {
mode: 'production',
optimization: {
minimize: true
}
};
NODE_ENV и tree
shakingTree shaking работает значительно эффективнее в production-режиме.
Webpack анализирует:
Пример:
if (process.env.NODE_ENV !== 'production') {
debugTools();
}
В production код становится недостижимым и удаляется.
Dead code elimination — удаление кода, который никогда не выполнится.
Пример:
const DEV = process.env.NODE_ENV !== 'production';
if (DEV) {
console.log('debug');
}
После замены:
const DEV = false;
if (false) {
console.log('debug');
}
Terser удаляет этот блок.
Многие библиотеки имеют условную логику:
if (process.env.NODE_ENV === 'development') {
validate();
}
Без правильного NODE_ENV:
Распространённая ошибка:
mode: 'production'
но:
process.env.NODE_ENV === 'development'
или наоборот.
Это создаёт рассинхронизацию между:
Рекомендуется использовать единое значение:
const NODE_ENV = process.env.NODE_ENV || 'development';
module.exports = {
mode: NODE_ENV
};
Пакет:
npm install dotenv --save-dev
Позволяет загружать переменные из .env.
.envNODE_ENV=production
API_URL=https://api.site.com
require('dotenv').config();
В браузере объекта process.env не существует.
Webpack имитирует его через подстановку значений.
Это означает:
console.log(process.env);
не выводит реальные переменные окружения ОС.
Пример:
console.log(process.env.NODE_ENV);
в webpack.config.js показывает реальное значение
окружения Node.js.
Но внутри клиентского кода:
console.log(process.env.NODE_ENV);
используется уже подставленное значение Webpack.
При:
mode: 'production'
Webpack автоматически включает:
| Оптимизация | Назначение |
|---|---|
| TerserPlugin | минификация |
| ModuleConcatenationPlugin | scope hoisting |
| tree shaking | удаление unused exports |
| deterministic ids | стабильный cache |
| sideEffects analysis | удаление лишнего кода |
Production-сборка:
Development-сборка:
Пример:
console.log(process.env.NODE_ENV);
Помимо NODE_ENV можно определять собственные
переменные:
new webpack.DefinePlugin({
__DEV__: JSON.stringify(true),
__API__: JSON.stringify('https://api.site.com')
})
Плохо:
if (window.location.hostname === 'localhost') {
enableDebug();
}
Лучше:
if (process.env.NODE_ENV === 'development') {
enableDebug();
}
Во втором случае debug-код может быть полностью удалён из production bundle.
Babel также ориентируется на NODE_ENV.
Пример:
presets: [
['@babel/preset-env']
]
Некоторые плагины автоматически отключаются в production.
Пример:
module.exports = {
plugins: [
process.env.NODE_ENV === 'production'
? require('cssnano')
: false
].filter(Boolean)
};
Vue также разделяет development и production-режимы.
Development включает:
Production удаляет эти части.
Angular CLI использует environment-конфигурации:
ng build --configuration production
Внутри также применяется production-режим и оптимизация сборки.
NODE_ENVНа сервере:
process.env.NODE_ENV
является реальной переменной окружения Node.js.
На клиенте — результатом подстановки Webpack.
Эти механизмы необходимо различать.
const webpack = require('webpack');
module.exports = {
mode: 'production',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
optimization: {
minimize: true
}
};
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
]
};
NODE_ENV| Эффект | Результат |
|---|---|
| Удаление debug-кода | меньше bundle |
| Включение production-режима библиотек | выше производительность |
| Tree shaking | удаление unused modules |
| Минификация | уменьшение размера |
| Отключение warning | ускорение runtime |
| Оптимизация React/Vue | меньше памяти и CPU |
mode: 'development'
в production-сборке.
JSON.stringifynew DefinePlugin({
NODE_ENV: 'production'
})
mode
и NODE_ENVmode: 'production'
но:
NODE_ENV=development
process.env без DefinePluginВ браузере:
console.log(process.env.NODE_ENV);
может привести к ошибкам, если подстановка не настроена.
{
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve",
"build": "cross-env NODE_ENV=production webpack"
}
}
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
mode: isProd ? 'production' : 'development'
};