DefinePlugin — встроенный плагин Webpack,
предназначенный для подстановки констант во время сборки. Чаще всего
используется для:
Плагин работает на этапе компиляции и выполняет текстовую замену идентификаторов внутри исходного кода.
Базовый пример:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
API_URL: JSON.stringify('https://api.site.com')
})
]
};
После сборки:
console.log(API_URL);
превратится в:
console.log('https://api.site.com');
DefinePlugin не создаёт настоящие переменные времени
выполнения. Он заменяет код ещё до запуска JavaScript.
Это означает:
if (DEBUG) {
console.log('debug');
}
может быть преобразовано в:
if (false) {
console.log('debug');
}
После этого минификатор (TerserPlugin) способен
полностью удалить мёртвый код.
Webpack уже содержит DefinePlugin.
const webpack = require('webpack');
Использование:
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify('1.0.0')
})
]
DefinePlugin вставляет код буквально.
Ошибка:
new webpack.DefinePlugin({
APP_NAME: 'My App'
});
Webpack воспримет My App как JavaScript-код.
Правильный вариант:
new webpack.DefinePlugin({
APP_NAME: JSON.stringify('My App')
});
Результат:
const title = "My App";
new webpack.DefinePlugin({
SITE_NAME: JSON.stringify('NWEB')
});
Исходный код:
console.log(SITE_NAME);
После сборки:
console.log("NWEB");
new webpack.DefinePlugin({
PORT: 3000
});
Использование:
console.log(PORT);
Результат:
console.log(3000);
new webpack.DefinePlugin({
IS_DEV: true
});
Пример:
if (IS_DEV) {
console.log('development');
}
Одна из главных задач DefinePlugin — условное удаление
кода.
Конфигурация:
new webpack.DefinePlugin({
__DEV__: JSON.stringify(false)
});
Код:
if (__DEV__) {
console.log('debug');
}
После минификации:
// код полностью удалён
Такой подход широко используется в:
Самый популярный сценарий — внедрение process.env.
Пример:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
});
Теперь:
console.log(process.env.NODE_ENV);
становится:
console.log("production");
process.env — объект Node.js.
В браузере:
console.log(process.env);
вызывает ошибку:
process is not defined
Webpack решает проблему через compile-time replacement.
Чаще всего:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
});
Запуск:
NODE_ENV=production webpack
или:
cross-env NODE_ENV=production webpack
if (process.env.NODE_ENV === 'production') {
enableAnalytics();
}
После сборки:
if ("production" === 'production') {
enableAnalytics();
}
Минификатор упростит выражение.
DefinePlugin позволяет создавать compile-time ветвление.
Пример:
if (process.env.NODE_ENV !== 'production') {
console.log('debug');
}
Production-сборка:
if ("production" !== 'production') {
console.log('debug');
}
После оптимизации:
// код удалён
Webpack способен удалять недостижимые ветки.
Пример:
if (FEATURE_X) {
import('./feature-x');
}
Конфигурация:
new webpack.DefinePlugin({
FEATURE_X: false
});
В production код feature-x может не попасть в bundle.
Допустим:
new webpack.DefinePlugin({
'process.env': JSON.stringify({
NODE_ENV: 'production',
API_URL: 'https://api.site.com'
})
});
Теперь:
console.log(process.env.API_URL);
Работает корректно.
Но такой способ опасен.
Плохая практика:
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env)
});
Проблемы:
Нужно передавать только необходимые переменные.
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'process.env.APP_VERSION': JSON.stringify(process.env.APP_VERSION)
});
Часто используется библиотека dotenv.
Установка:
npm install dotenv
Файл:
API_URL=https://api.site.com
APP_NAME=NWEB
Конфигурация:
require('dotenv').config();
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'process.env.APP_NAME': JSON.stringify(process.env.APP_NAME)
});
Пример:
const webpack = require('webpack');
const dotenv = require('dotenv');
dotenv.config();
const envKeys = Object.keys(process.env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(process.env[next]);
return prev;
}, {});
module.exports = {
plugins: [
new webpack.DefinePlugin(envKeys)
]
};
Лучше передавать только публичные переменные.
Например:
PUBLIC_API_URL=https://api.site.com
PUBLIC_THEME=dark
DB_PASSWORD=secret
JWT_SECRET=token
Конфигурация:
const envKeys = Object.keys(process.env)
.filter(key => key.startsWith('PUBLIC_'))
.reduce((prev, key) => {
prev[`process.env.${key}`] = JSON.stringify(process.env[key]);
return prev;
}, {});
Часто конфигурация зависит от режима.
module.exports = (env, argv) => {
const isProd = argv.mode === 'production';
return {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(
isProd ? 'production' : 'development'
)
})
]
};
};
Feature flags позволяют включать функции без изменения кода.
new webpack.DefinePlugin({
ENABLE_CHAT: true,
ENABLE_ADMIN: false
});
Пример:
if (ENABLE_CHAT) {
initChat();
}
new webpack.DefinePlugin({
APP_VERSION: JSON.stringify('2.5.1'),
BUILD_DATE: JSON.stringify(new Date().toISOString())
});
Использование:
console.log(APP_VERSION);
console.log(BUILD_DATE);
const packageJson = require('./package.json');
new webpack.DefinePlugin({
APP_VERSION: JSON.stringify(packageJson.version)
});
const childProcess = require('child_process');
const commitHash = childProcess
.execSync('git rev-parse --short HEAD')
.toString()
.trim();
new webpack.DefinePlugin({
COMMIT_HASH: JSON.stringify(commitHash)
});
React использует process.env.NODE_ENV.
В production:
if (process.env.NODE_ENV !== 'production') {
warning();
}
Webpack заменяет значение, после чего React удаляет dev-only код.
Это уменьшает размер bundle.
Vue также использует compile-time flags.
Пример:
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
});
EnvironmentPlugin — упрощённая оболочка над
DefinePlugin.
Пример:
new webpack.EnvironmentPlugin([
'NODE_ENV',
'API_URL'
]);
Эквивалентно:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.API_URL': JSON.stringify(process.env.API_URL)
});
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
API_URL: 'http://localhost:3000'
});
Ошибка:
new webpack.DefinePlugin({
API_URL: 'https://api.site.com'
});
Результат:
https://api.site.com
Webpack интерпретирует это как код.
Плохо:
new webpack.DefinePlugin({
window: {}
});
Можно случайно сломать глобальные объекты браузера.
DefinePlugin работает только во время сборки.
Нельзя:
fetch(process.env.API_URL);
и ожидать изменения значения без пересборки.
DefinePlugin создаёт compile-time конфигурацию.
Это означает:
Runtime-конфигурация обычно реализуется через:
В SSR важно разделять:
Нельзя передавать в браузер:
Безопасно:
new webpack.DefinePlugin({
'process.env.PUBLIC_API_URL': JSON.stringify(
process.env.PUBLIC_API_URL
)
});
Webpack поддерживает специальный синтаксис:
new webpack.DefinePlugin({
'typeof window': JSON.stringify('object')
});
Редкий, но полезный сценарий.
Плагин помогает:
const webpack = require('webpack');
const dotenv = require('dotenv');
dotenv.config();
module.exports = {
mode: 'production',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.API_URL': JSON.stringify(
process.env.API_URL
),
'process.env.APP_VERSION': JSON.stringify(
process.env.npm_package_version
),
__DEV__: false,
ENABLE_ANALYTICS: true
})
]
};
if (__DEV__) {
console.log('debug mode');
}
if (ENABLE_ANALYTICS) {
analytics.init();
}
fetch(`${process.env.API_URL}/users`)
.then(response => response.json())
.then(console.log);
console.log(process.env.APP_VERSION);
Распространённая структура:
.env
.env.development
.env.production
.env.local
Каждый файл содержит собственные значения:
API_URL=https://dev-api.site.com
или:
API_URL=https://prod-api.site.com
npm install dotenv-flow
Конфигурация:
require('dotenv-flow').config();
dotenv-flow автоматически подгружает нужный env-файл в
зависимости от режима.
if (!process.env.API_URL) {
throw new Error('API_URL is required');
}
В TypeScript:
declare namespace NodeJS {
interface ProcessEnv {
API_URL: string;
NODE_ENV: 'development' | 'production';
}
}
В monorepo env-переменные часто централизуются:
/packages
/apps
/config
Общий env-loader:
const sharedEnv = require('../. ./config/env');
function getClientEnvironment(prefix) {
return Object.keys(process.env)
.filter(key => key.startsWith(prefix))
.reduce((env, key) => {
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
return env;
}, {});
}
Использование:
new webpack.DefinePlugin(
getClientEnvironment('PUBLIC_')
);
process.env;JSON.stringify для строк;