Передача зависящих от среды переменных в веб-пакет
Я пытаюсь преобразовать угловое приложение из gulp в webpack. в gulp я использую gulp-preprocess для замены некоторых переменных на html-странице (например, имя базы данных) в зависимости от NODE_ENV. Каков наилучший способ достижения аналогичного результата с помощью веб-пакета?
18 ответов
Есть два основных способа добиться этого.
DefinePlugin
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),
Обратите внимание, что это просто заменит совпадения "как есть". Вот почему строка в том формате, в котором она есть. У вас может быть более сложная структура, например, объект там, но вы поймете идею.
EnvironmentPlugin
new webpack.EnvironmentPlugin(['NODE_ENV'])
EnvironmentPlugin
использования DefinePlugin
внутренне и отображает значения среды для кодирования через него. Терсер синтаксис.
кличка
В качестве альтернативы вы можете использовать конфигурацию через модуль с псевдонимами. Со стороны потребителя это будет выглядеть так:
var config = require('config');
Сама конфигурация может выглядеть так:
resolve: {
alias: {
config: path.join(__dirname, 'config', process.env.NODE_ENV)
}
}
Скажем process.env.NODE_ENV
является development
, Это будет отображаться в ./config/development.js
затем. Модуль, к которому он привязан, может экспортировать конфигурацию следующим образом:
module.exports = {
testing: 'something',
...
};
Просто еще один вариант, если вы хотите использовать только интерфейс Cli, просто используйте define
вариант веб-пакета. Я добавляю следующий скрипт в мой package.json
:
"build-production": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors"
Так что мне просто нужно бежать npm run build-production
,
Я исследовал несколько вариантов того, как устанавливать переменные, специфичные для окружающей среды, и получил следующее:
У меня есть 2 конфига webpack в настоящее время:
webpack.production.config.js
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production'),
'API_URL': JSON.stringify('http://localhost:8080/bands')
}
}),
webpack.config.js
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('development'),
'API_URL': JSON.stringify('http://10.10.10.10:8080/bands')
}
}),
В моем коде я получаю значение API_URL таким (кратким) способом:
const apiUrl = process.env.API_URL;
РЕДАКТИРОВАТЬ 3 ноября 2016
В документации по Webpack есть пример: https://webpack.js.org/plugins/define-plugin/
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})
С ESLint вам нужно специально разрешить неопределенные переменные в коде, если у вас есть no-undef
Править на. http://eslint.org/docs/rules/no-undef вот так:
/*global TWO*/
console.log('Running App version ' + TWO);
РЕДАКТИРОВАТЬ 7 сентября 2017 г. (специфично для Create-React-App)
Если вам не нужно слишком много настраивать, проверьте Create-React-App: Create-React-App - Добавление переменных пользовательской среды. Под капотом CRA все равно использует Webpack.
Вы можете передать любой аргумент командной строки без дополнительных плагинов, используя --env
начиная с веб-пакета 2:
webpack --config webpack.config.js --env.foo=bar
Используя переменную в webpack.config.js:
module.exports = function(env) {
if (env.foo === 'bar') {
// do something
}
}
Вы можете напрямую использовать EnvironmentPlugin
доступно в webpack
иметь доступ к любой переменной среды во время транспортировки.
Вы просто должны объявить плагин в своем webpack.config.js
файл:
var webpack = require('webpack');
module.exports = {
/* ... */
plugins = [
new webpack.EnvironmentPlugin(['NODE_ENV'])
]
};
Обратите внимание, что вы должны явно объявить имя переменных среды, которые вы хотите использовать.
Чтобы добавить к куче ответов лично, я предпочитаю следующее:
const webpack = require('webpack');
const prod = process.argv.indexOf('-p') !== -1;
module.exports = {
...
plugins: [
new webpack.DefinePlugin({
process: {
env: {
NODE_ENV: prod? `"production"`: '"development"'
}
}
}),
...
]
};
Используя это, вы не столкнетесь с проблемой фан-переменных env или кроссплатформенности (с env vars) Все, что вы делаете, это бегите нормально webpack
или же webpack -p
для разработки или производства соответственно.
Ссылка: выпуск Github
Так как мой Редактировать вышеупомянутый пост от thevangelist не был одобрен, публикуя дополнительную информацию.
Если вы хотите выбрать значение из package.json как определенный номер версии и получить к нему доступ через DefinePlugin внутри Javascript.
{"version": "0.0.1"}
Затем импортируйте package.json внутри соответствующего webpack.config, получите доступ к атрибуту с помощью переменной import, затем используйте атрибут в DefinePlugin.
const PACKAGE = require('../package.json');
const _version = PACKAGE.version;//Picks the version number from package.json
Например, определенная конфигурация в webpack.config использует METADATA для DefinePlugin:
const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, {
host: HOST,
port: PORT,
ENV: ENV,
HMR: HMR,
RELEASE_VERSION:_version//Version attribute retrieved from package.json
});
new DefinePlugin({
'ENV': JSON.stringify(METADATA.ENV),
'HMR': METADATA.HMR,
'process.env': {
'ENV': JSON.stringify(METADATA.ENV),
'NODE_ENV': JSON.stringify(METADATA.ENV),
'HMR': METADATA.HMR,
'VERSION': JSON.stringify(METADATA.RELEASE_VERSION)//Setting it for the Scripts usage.
}
}),
Получите доступ к этому внутри любого машинописного файла:
this.versionNumber = process.env.VERSION;
Самый умный способ будет выглядеть так:
// webpack.config.js
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify(require("./package.json").version)
})
]
Просто еще один ответ, который похож на ответ @zer0chain. Однако с одним отличием.
настройка webpack -p
достаточно.
Это так же, как:
--define process.env.NODE_ENV="production"
И это так же, как
// webpack.config.js
const webpack = require('webpack');
module.exports = {
//...
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
Так что вам может понадобиться только что-то подобное в package.json
Файл узла:
{
"name": "projectname",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"debug": "webpack -d",
"production": "webpack -p"
},
"author": "prosti",
"license": "ISC",
"dependencies": {
"webpack": "^2.2.1",
...
}
}
Несколько советов от DefinePlugin:
DefinePlugin позволяет вам создавать глобальные константы, которые можно настроить во время компиляции. Это может быть полезно для разрешения различного поведения между сборками разработки и выпусками сборки. Например, вы можете использовать глобальную константу, чтобы определить, происходит ли запись в журнал; возможно, вы выполняете вход в свою сборку разработки, но не в сборке выпуска. Именно такой сценарий облегчает использование DefinePlugin.
Что это так, чтобы вы могли проверить, если вы печатаете webpack --help
Config options:
--config Path to the config file
[string] [default: webpack.config.js or webpackfile.js]
--env Enviroment passed to the config, when it is a function
Basic options:
--context The root directory for resolving entry point and stats
[string] [default: The current directory]
--entry The entry point [string]
--watch, -w Watch the filesystem for changes [boolean]
--debug Switch loaders to debug mode [boolean]
--devtool Enable devtool for better debugging experience (Example:
--devtool eval-cheap-module-source-map) [string]
-d shortcut for --debug --devtool eval-cheap-module-source-map
--output-pathinfo [boolean]
-p shortcut for --optimize-minimize --define
process.env.NODE_ENV="production"
[boolean]
--progress Print compilation progress in percentage [boolean]
Я обнаружил, что следующее решение проще всего настроить переменную среды для Webpack 2:
Например, у нас есть настройки веб-пакета:
var webpack = require('webpack')
let webpackConfig = (env) => { // Passing envirmonment through
// function is important here
return {
entry: {
// entries
},
output: {
// outputs
},
plugins: [
// plugins
],
module: {
// modules
},
resolve: {
// resolves
}
}
};
module.exports = webpackConfig;
Добавить переменную среды в Webpack:
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
]
Определите переменную плагина и добавьте ее в plugins
:
new webpack.DefinePlugin({
'NODE_ENV': JSON.stringify(env.NODE_ENV || 'development')
}),
Теперь при запуске команды webpack передайте env.NODE_ENV
в качестве аргумента:
webpack --env.NODE_ENV=development
// OR
webpack --env.NODE_ENV development
Теперь вы можете получить доступ NODE_ENV
переменная в любом месте вашего кода.
Мой обходной путь для версии webpack
"webpack": "^4.29.6"
очень просто.
//package.json
{
...
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --open --mode development"
},
}
вы можете передать параметр --mode с помощью команды webpack, а затем в webpack.config.js
// webpack.config.json
module.exports = (env,argv) => {
return {
...
externals: {
// global app config object
config: JSON.stringify({
apiUrl: (argv.mode==="production") ? '/api' : 'localhost:3002/api'
})
}
}
И я использую baseurl в своем коде, как это
// my api service
import config from 'config';
console.log(config.apiUrl) // like fetch(`${config.apiUrl}/users/user-login`)
Я предпочитаю использовать.env файл для другой среды.
- Используйте webpack.dev.config для копирования
env.dev
зайти в корневую папку - Используйте webpack.prod.config для копирования
env.prod
в.env
и в коде
использование
require('dotenv').config();
const API = process.env.API ## which will store the value from .env file
Чтобы добавить к куче ответов:
Используйте ExtendedDefinePlugin вместо DefinePlugin
npm install extended-define-webpack-plugin --save-dev.
ExtendedDefinePlugin гораздо проще в использовании и задокументирована:-) ссылка
Поскольку DefinePlugin не хватает хорошей документации, я хочу помочь, сказав, что на самом деле он работает как #DEFINE в C#.
#if (DEBUG)
Console.WriteLine("Debugging is enabled.");
#endif
Таким образом, если вы хотите понять, как работает DefinePlugin, прочтите документацию C# #define. ссылка на сайт
dotenv-веб-пакет
Защищенный плагин для веб-пакетов, который поддерживает dotenv и другие переменные среды и показывает только то, что вы выбираете и используете.
с некоторым обходным путем с конфигурацией, основанной на опции для достижения этого, как только пакет имеет файл в качестве начальных значений для переменных env, вы можете использовать его для
development
и пусть для вашего производства.
Применение
- установить пакет
npm install dotenv-webpack --save-dev
- Создать
.env.defaults
файл
API_URL='dev_url/api/'
- создать файл оставить его пустым, пусть
defaults
работает, обновите его в процессе развертывания - конфигурация
webpack
-webpack.config.js
new Dotenv({
defaults: true
})
- тест среды разработки
console.log(process.env.API_URL)
// Outputs: dev_url/api/
- при сборке обновить пусто
.env
файл
API_URL='prod_url/api/'
dotenv-webpack будет использовать это для переопределения
env.defaults
- тестовая среда
file.js
console.log(process.env.API_URL)
// Outputs: prod_url/api/
Начиная с Webpack v4, просто настройка mode
в вашем конфиге Webpack установит NODE_ENV
для вас (через DefinePlugin
). Документы здесь.
Вот способ, который сработал для меня и позволил мне сохранить мои переменные среды DRY, повторно используя файл json.
let config = require('./settings.json');
if (__PROD__) {
config = require('./settings-prod.json');
}
const envVars = {};
Object.keys(config).forEach((key) => {
envVars[key] = JSON.stringify(config[key]);
});
new webpack.DefinePlugin({
'process.env': envVars
}),
Теперь 2020, я столкнулся с тем же вопросом, но на этот старый вопрос так много новых ответов, просто перечислите некоторые из них:
- это webpack.config.js
plugins: [
new HtmlWebpackPlugin({
// 1. title is the parameter, you can use in ejs template
templateParameters:{
title: JSON.stringify(someting: 'something'),
},
}),
//2. BUILT_AT is a parameter too. can use it.
new webpack.DefinePlugin({
BUILT_AT: webpack.DefinePlugin.runtimeValue(Date.now,"some"),
}),
//3. for webpack5, you can use global variable: __webpack_hash__
//new webpack.ExtendedAPIPlugin()
],
//4. this is not variable, this is module, so use 'import tt' to use it.
externals: {
'ex_title': JSON.stringify({
tt: 'eitentitle',
})
},
4 способа - только основные, я верю, что есть еще больше способов. но я думаю, может быть, этот 4 ways самый простой.
Я не большой поклонник...
new webpack.DefinePlugin({
'process.env': envVars
}),
... так как он не обеспечивает никакой безопасности. вместо этого вы заканчиваете тем, что усиливаете свои секретные вещи, если только вы не добавите веб-пакет в gitignore - есть лучшее решение.
В основном с этим конфигом, как только вы скомпилируете свой код, все переменные процесса env будут удалены из всего кода, не будет ни одного process.env.VAR благодаря плагину babel transform-inline-environment-variables
PS Если вы не хотите заканчивать целой кучей неопределенностей, убедитесь, что вы вызываете env.js до того, как webpack вызывает babel-loader, поэтому это первое, что вызывает webpack. массив vars в файле babel.config.js должен соответствовать объекту в env.js. теперь есть только одна вещь, которую нужно сделать. добавить .env
В файл помещаются все переменные env, файл должен находиться в корне проекта, или вы можете свободно добавлять его туда, где хотите, просто установите одно и то же место в файле env.js и добавьте его в gitignore.
const dotFiles = ['.env'].filter(Boolean);
if (existsSync(dotFiles)) {
require("dotenv-expand")(require("dotenv").config((dotFiles)));
}
Если вы хотите увидеть весь babel + webpack + ts, возьмите его с ногhttps://github.com/EnetoJara/Node-typescript-babel-webpack.git
и та же логика применяется, чтобы реагировать и все остальные
config
---webpack.js
---env.js
src
---source code world
.env
bunch of dotFiles
env.js
"use strict";
/***
I took the main idea from CRA, but mine is more cooler xD
*/
const {realpathSync, existsSync} = require('fs');
const {resolve, isAbsolute, delimiter} = require('path');
const NODE_ENV = process.env.NODE_ENV || "development";
const appDirectory = realpathSync(process.cwd());
if (typeof NODE_ENV !== "string") {
throw new Error("falle and stuff");
}
const dotFiles = ['.env'].filter(Boolean);
if (existsSync(dotFiles)) {
require("dotenv-expand")(require("dotenv").config((dotFiles)));
}
process.env.NODE_PATH = (process.env.NODE_PATH || "")
.split(delimiter)
.filter(folder => folder && isAbsolute(folder))
.map(folder => resolve(appDirectory, folder))
.join(delimiter);
const ENETO_APP = /^ENETO_APP_/i;
module.exports = (function () {
const raw = Object.keys ( process.env )
.filter ( key => ENETO_APP.test ( key ) )
.reduce ( ( env, key ) => {
env[ key ] = process.env[ key ];
return env;
},
{
BABEL_ENV: process.env.ENETO_APP_BABEL_ENV,
ENETO_APP_DB_NAME: process.env.ENETO_APP_DB_NAME,
ENETO_APP_DB_PASSWORD: process.env.ENETO_APP_DB_PASSWORD,
ENETO_APP_DB_USER: process.env.ENETO_APP_DB_USER,
GENERATE_SOURCEMAP: process.env.ENETO_APP_GENERATE_SOURCEMAP,
NODE_ENV: process.env.ENETO_APP_NODE_ENV,
PORT: process.env.ENETO_APP_PORT,
PUBLIC_URL: "/"
} );
const stringyField = {
"process.env": Object.keys(raw).reduce((env, key)=> {
env[key]=JSON.stringify(raw[key]);
return env;
},{}),
};
return {
raw, stringyField
}
})();
файл webpack без плагинов тролль
"use strict";
require("core-js");
require("./env.js");
const path = require("path");
const nodeExternals = require("webpack-node-externals");
module.exports = env => {
return {
devtool: "source-map",
entry: path.join(__dirname, '../src/dev.ts'),
externals: [nodeExternals()],
module: {
rules: [
{
exclude: /node_modules/,
test: /\.ts$/,
use: [
{
loader: "babel-loader",
},
{
loader: "ts-loader"
}
],
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
},
],
},
],
},
node: {
__dirname: false,
__filename: false,
},
optimization: {
splitChunks: {
automaticNameDelimiter: "_",
cacheGroups: {
vendor: {
chunks: "initial",
minChunks: 2,
name: "vendor",
test: /[\\/]node_modules[\\/]/,
},
},
},
},
output: {
chunkFilename: "main.chunk.js",
filename: "name-bundle.js",
libraryTarget: "commonjs2",
},
plugins: [],
resolve: {
extensions: ['.ts', '.js']
} ,
target: "node"
};
};
babel.config.js
module.exports = api => {
api.cache(() => process.env.NODE_ENV);
return {
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-transform-classes", {loose: true}],
["@babel/plugin-external-helpers"],
["@babel/plugin-transform-runtime"],
["@babel/plugin-transform-modules-commonjs"],
["transform-member-expression-literals"],
["transform-property-literals"],
["@babel/plugin-transform-reserved-words"],
["@babel/plugin-transform-property-mutators"],
["@babel/plugin-transform-arrow-functions"],
["@babel/plugin-transform-block-scoped-functions"],
[
"@babel/plugin-transform-async-to-generator",
{
method: "coroutine",
module: "bluebird",
},
],
["@babel/plugin-proposal-async-generator-functions"],
["@babel/plugin-transform-block-scoping"],
["@babel/plugin-transform-computed-properties"],
["@babel/plugin-transform-destructuring"],
["@babel/plugin-transform-duplicate-keys"],
["@babel/plugin-transform-for-of"],
["@babel/plugin-transform-function-name"],
["@babel/plugin-transform-literals"],
["@babel/plugin-transform-object-super"],
["@babel/plugin-transform-shorthand-properties"],
["@babel/plugin-transform-spread"],
["@babel/plugin-transform-template-literals"],
["@babel/plugin-transform-exponentiation-operator"],
["@babel/plugin-proposal-object-rest-spread"],
["@babel/plugin-proposal-do-expressions"],
["@babel/plugin-proposal-export-default-from"],
["@babel/plugin-proposal-export-namespace-from"],
["@babel/plugin-proposal-logical-assignment-operators"],
["@babel/plugin-proposal-throw-expressions"],
[
"transform-inline-environment-variables",
{
include: [
"ENETO_APP_PORT",
"ENETO_APP_NODE_ENV",
"ENETO_APP_BABEL_ENV",
"ENETO_APP_DB_NAME",
"ENETO_APP_DB_USER",
"ENETO_APP_DB_PASSWORD",
],
},
],
],
presets: [["@babel/preset-env",{
targets: {
node: "current",
esmodules: true
},
useBuiltIns: 'entry',
corejs: 2,
modules: "cjs"
}],"@babel/preset-typescript"],
};
};
Я не знаю почему, но никто не упоминает самое простое решение. Это работает для меня для nodejs и grunt. Поскольку для многих людей веб-пакет может сбить с толку, вы можете просто использовать следующую строку:
process.env.NODE_ENV = 'production';
С вышеупомянутым решением вам не нужно использовать envify или webpack. Иногда простое жестко закодированное решение может работать для некоторых людей.