在webpack中传递环境变量


306

我正在尝试将角度应用程序从gulp转换为webpack。在gulp中,我使用gulp-preprocess来替换html页面中的某些变量(例如,数据库名称),具体取决于NODE_ENV。使用webpack达到类似结果的最佳方法是什么?


1
别名对您有用吗?
JuhoVepsäläinen15年

1
@bebraw:在我能够理解别名之前,我实现了您基于DefinePlugin()建议的其他解决方案。我现在确实看到别名将是一个更好的解决方案,并且可能会在某个时候进行重构-谢谢。如果您想在答案中包含两个解决方案,我会很乐意接受。
kpg 2015年

2
通过控制台消息定向到此处。如何在Browserify中解决此问题?
GN。

2
这个问题是尝试在构建时还是在加载时配置SPA?我注意到SPA的两种配置类型:1)开发或生产模式,以及2)部署环境,例如开发,登台,生产。我认为NODE_ENV可在构建时用于配置(1),但在部署时如何配置(2),例如为不同的部署环境配置生产模式。我希望这与这个问题有关。
Ashley Aitken

1
@AshleyAitken大的问题,而我无法找到在此线程(也许我错过了)的答案,但张贴了这个新的线程:stackoverflow.com/questions/44464504/...
大卫·特萨

Answers:


427

有两种基本方法可以实现此目的。

定义插件

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

请注意,这只会按原样替换匹配项。这就是字符串具有其格式的原因。您可能有一个更复杂的结构,例如那里的一个对象,但您明白了。

环境插件

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPluginDefinePlugin内部使用并映射环境值以通过它进行编码。Terser语法。

别名

或者,您可以通过别名模块使用配置。从消费者方面来看,它看起来像这样:

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',
    ...
};

3
感谢您指出事实,它“按原样”替换了匹配项。我花了一段时间才弄清楚为什么我的代码抛出错误,这是因为我没有将值包装在JSON.stringify()
pbojinov

4
如果您使用的是ES2015,则还可以使用字符串插值'process.env.NODE_ENV': `"${process.env.NODE_ENV || 'development'}"`
user2688473'2

1
@ tybro0103 JSON.stringify('development')可能不是真正有用。相反,JSON.stringify(someVariable)可以相当!
superjos

1
您应该NODE_ENV这样做。如何设置取决于您的平台。
JuhoVepsäläinen16年

1
@AnyulRivas是的。React使用process.env.NODE_ENV模式并且有效。
JuhoVepsäläinen17年

109

另一个选择,如果您只想使用cli界面,请使用definewebpack 的选择。我将以下脚本添加到我的package.json

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

所以我只需要跑步npm run build-production


2
是否有相关文档?我无法使用Google --define :(
理查德

5
对于webpack @ 2,“-p”已经是--optimize-minimize --define process.env.NODE_ENV =“ production”
okm

@okm文档提到-p等于--optimize-minimize --optimize-occurence-order,因此没有提及--define process.env.NODE_ENV =“ production”。它被删除了吗?
Nader Ghanbari

1
@NaderHadjiGhanbari它在webpack版本2中webpack.js.org/api/cli/#shortcuts
okm 2016年

73

我研究了关于如何设置特定于环境的变量的几个选项,并最终得出以下结论:

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

编辑2017年9月7日(特定于Create-React-App)

如果您不想进行过多配置,请查看Create-React-App:Create-React-App-添加自定义环境变量。CRA仍然在使用Webpack。


2
您是否发现这阻止了在运行时传递任何环境变量?如果您替换了全部,process.env那么在webpack构建期间process.env.PORT是否无法解决问题,undefined这意味着您不能再从环境中覆盖端口了?
djskinner'8

非常感谢。最后,这个问题的答案是可以理解的!
戴夫·萨格

什么是过程?它来自哪里?如果它是节点对象,它将如何进入浏览器?
Daniel Birowsky Popeski '16

这是一个糟糕的解决方案,除了设置NODE_ENV和API_URL之外,您有两个几乎完全相同的webpack.configs
Brian Ogden

1
@BrianOgden是的,的确如此,您应该使用类似webpack-merge的方法:npmjs.com/package/webpack-merge-这个问题IMO有点超出范围。
thevangelist

24

你可以通过任何命令行参数,无需额外的插件使用--env以来的WebPack 2:

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

在webpack.config.js中使用变量:

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

资源


22

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

您只需要在webpack.config.js文件中声明插件:

var webpack = require('webpack');

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

请注意,您必须明确声明要使用的环境变量的名称。


4
webpack文档中有一个带有这种用例的示例。github.com/webpack/docs/wiki/list-of-plugins#environmentplugin
Technetium

1
如果要将环境变量放在.env文件中,则可以使用dotenv包并在webpack.config.js中对其进行初始化。npmjs.com/package/dotenv
Justin McCandless

13

要亲自添加到答案中,我更喜欢以下内容:

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

参考:Github问题


当定义过程值倾向于'process.env.NODE_ENV': JSON.stringify('production')过度process: { env: { NODE_ENV: JSON.stringify('production') } }。使用后者会覆盖过程对象,这可能会破坏与期望在过程对象上定义其他值的某些模块的兼容性。
slorenzo

13

由于我对thevangelist上述帖子的“编辑” 未获批准,因此请发布其他信息。

如果要从package.json中选择值(如定义的版本号),然后通过Javascript中的DefinePlugin访问它。

{"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)
    })
  ]

感谢罗斯·艾伦


11

只是与@ 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.jsonNode文件中添加以下内容:

{
  "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]

3

要添加一堆答案:

使用ExtendedDefinePlugin而不是DefinePlugin

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

ExtendedDefinePlugin的使用更加简单,并记录了:-) 链接

因为DefinePlugin 缺少好的文档,所以我想通过说它实际上像c#中的#DEFINE来提供帮助。

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

因此,如果您想了解DefinePlugin的工作原理,请阅读c##define文档。链接


2

我更喜欢将.env文件用于不同的环境。

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

和在代码中

采用

require('dotenv').config(); const API = process.env.API ## which will store the value from .env file


2

我发现以下解决方案最容易为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',
       }),
]

定义插件变量并将其添加到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在代码中的任何位置访问变量。



1

这是一种对我有用的方法,它允许我通过重用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
}),

0

我不是...的忠实粉丝

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

...因为它不提供任何类型的安全性。相反,您最终会增加秘密内容,除非您向gitignore add‍♀️添加webpack,否则会有更好的解决方案。

基本上,使用此配置,一旦您编译了代码,所有进程环境变量将从整个代码中删除,就不会有单个process.env.VAR了,因为babel插件transform-inline-environment-variables PS如果您不想结束由于存在很多不确定性,请确保在webpack调用babel-loader之前先调用env.js,这就是为什么它是webpack调用的第一件事。babel.config.js文件中的vars数组必须与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"],
    };
};

“除非您将Webpack添加到gitignore,否则您最终会增加秘密内容。” @Ernesto可以扩展一下吗?
凯蒂·拜尔斯

基本上,您的捆绑包最终没有process.env.BLAHBLAH并放入实际值。例如,不是以process.env.NODE_ENV结尾,而是以“生产”结尾,而是说这不是最佳示例,而是想象一个秘密密钥。您的捆绑包将具有实际值,并且谁知道有线串代表🤷‍♀️
Ernesto

嗯-是的,这些值将在插的版本,但想必你不推,为GitHub的...
凯蒂·拜尔斯

-4

我不知道为什么,但是没有人真正提到最简单的解决方案。这对我来说适用于nodejs和grunt。对于许多人来说,Webpack可能会使您感到困惑,您只需使用以下行:

process.env.NODE_ENV = 'production';

使用上述解决方案,您实际上不需要使用envify或webpack。有时,简单的硬编码解决方案可能对某些人有用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.