在webpack中传递环境相关变量

我正在尝试将一个angular应用程序从gulp转换为webpack。在gulp中,我使用gulp预处理来替换html页面中的一些变量(例如数据库名称),这取决于NODE_ENV。用webpack实现类似结果的最佳方法是什么?

353229 次浏览

有两种基本方法可以实现这一点。

DefinePlugin

new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),

请注意,这将只是替换匹配"as is"这就是为什么字符串是这样的格式。你可以有一个更复杂的结构,比如一个物体但你懂的。

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_ENVdevelopment。它将映射到./config/development.js。它映射到的模块可以像这样导出配置:

module.exports = {
testing: 'something',
...
};

只是另一个选项,如果你只想使用cli界面,只需使用webpack的define选项。我在package.json中添加了以下脚本:

"build-production": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors"

所以我只需要运行npm run build-production

你可以直接使用webpack中可用的EnvironmentPlugin在编译过程中访问任何环境变量。

你只需要在你的webpack.config.js文件中声明这个插件:

var webpack = require('webpack');


module.exports = {
/* ... */
plugins: [
new webpack.EnvironmentPlugin(['NODE_ENV'])
]
};

注意,必须显式声明要使用的环境变量的名称。

我研究了几个关于如何设置特定于环境的变量的选项,最后得到了这样的结果:

我目前有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;

编辑2016年11月3日

Webpack文档有一个例子:https://webpack.js.org/plugins/define-plugin/#usage

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);

EDIT 2017年9月7日(Create-React-App特定)

如果你不喜欢配置太多,请查看Create-React-App: Create-React-App -添加自定义环境变量。实际上CRA使用Webpack。

就我个人而言,我更喜欢以下这些答案:

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变量)。你所要做的就是分别为开发或生产运行正常的webpackwebpack -p

参考:Github的问题

你可以使用--env传递环境变量 无需额外插件

Webpack 2-4

webpack --config webpack.config.js --env.foo=bar

Webpack 5 + (without.)

webpack --config webpack.config.js --env foo=bar

然后,使用webpack.config.js中的变量:

module.exports = function(env) {
if (env.foo === 'bar') {
// do something
}
}

进一步的阅读: Webpack 2.0不支持自定义命令行参数? # 2254 < / > < / p >

再给这些问题补充一个答案:

使用ExtendedDefinePlugin代替DefinePlugin

npm install extended-define-webpack-plugin --save-dev.

ExtendedDefinePlugin使用起来更简单,并且有文档记录:-) 链接 < / p >

因为DefinePlugin 缺乏是很好的文档,我想通过说它实际上像#定义在c#中一样工作来帮助解决这个问题。

#if (DEBUG)
Console.WriteLine("Debugging is enabled.");
#endif

因此,如果你想了解DefinePlugin是如何工作的,请阅读c# #define文档。链接

我更喜欢使用。env文件不同的环境。

  1. 使用webpack.dev.config将env.dev复制到。env到根文件夹
  2. 使用webpack.prod.config将env.prod复制到.env

在代码中

使用

< p > <代码>要求(dotenv) . config (); const API = process.env.API ##,它将存储.env文件中的值 < /代码> < / p >

因为我的编辑以上的帖子被thevangelist没有被批准,张贴额外的信息。

如果你想从package.json中选取值,如定义的< / em > < em >版本号,并通过Javascript中的DefinePlugin访问它。

{"version": "0.0.1"}

然后,在各自的webpack.config中导入package.json,使用导入变量访问属性,然后在DefinePlugin中使用该属性。

const PACKAGE = require('../package.json');
const _version = PACKAGE.version;//Picks the version number from package.json

例如,webpack.config上的某些配置正在为DefinePlugin使用METADATA:

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.
}
}),

在任何typescript文件中访问它:

this.versionNumber = process.env.VERSION;

最聪明的方法是这样的:

// webpack.config.js
plugins: [
new webpack.DefinePlugin({
VERSION: JSON.stringify(require("./package.json").version)
})
]

感谢Ross Allen

这是另一个类似于@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设置环境变量:

例如,我们有一个webpack设置:

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',
}),
]

定义Plugin变量并将其添加到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变量。

这里有一种方法,对我来说是有效的,并允许我通过重用json文件保持我的环境变量DRY。

const webpack = require('webpack');
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
}),

从Webpack v4开始,在你的Webpack配置中简单地设置mode将为你设置NODE_ENV(通过DefinePlugin)。文档。

我不太喜欢……

new webpack.DefinePlugin({
'process.env': envVars
}),

...因为它不提供任何类型的安全性。相反,你最终会提升你的秘密东西,除非你添加一个webpack gitignore🤷‍♀️有一个更好的解决方案。

基本上,通过这个配置,一旦你编译了你的代码,所有的进程env变量将从整个代码中删除,不会有一个单独的process.env. var,这要感谢babel插件transform-inline-environment-variables PS:如果你不想有一大堆未定义,确保你在webpack调用babel-loader之前调用env.js,这就是为什么它是webpack调用的第一个东西。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从heaw得到它 https://github.com/EnetoJara/Node-typescript-babel-webpack.git < / p >

同样的逻辑也适用于react和所有其他💩

config
---webpack.js
---env.js
src
---source code world
.env
bunch of dotFiles

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"],
};
};

现在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种方法只是基本的,我相信还有更多的方法。但我认为这是最简单的方法。

我的webpack版本"webpack": "^4.29.6"的解决方法非常简单。

//package.json
{
...
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --open --mode development"
},
}

你可以在webpack命令中传递——mode参数,然后在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`)

dotenv-webpack

一个安全的webpack插件,支持dotenv和其他环境变量,只公开您选择和使用的内容。

通过一些基于defaults选项的配置的变通方法来实现这一点,一旦包有.env.defaults文件作为env变量的初始值,你就可以将它用于development,并让.env用于你的生产。

使用

  • 安装包
npm install dotenv-webpack --save-dev
  • 创建一个.env.defaults文件
API_URL='dev_url/api/'
  • 创建一个.env文件,将其保留为空,让defaults工作,在部署过程中更新它
  • config webpack - webpack.config.js . config
new Dotenv({
defaults: true
})
  • 开发环境测试file.js
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/
< p > dotenv-webpack
dotenv-defaults < / p >