在 webpack 中传递环境相关变量

Passing environment-dependent variables in webpack

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

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

定义插件

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

请注意,这只会“按原样”替换匹配项。这就是字符串采用原格式的原因。你可以有一个更复杂的结构,比如那里的一个对象,但你明白了。

环境插件

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPlugin 在内部使用 DefinePlugin 并将环境值映射到代码中。更简洁的语法。

别名

或者您可以通过 an aliased module 使用配置。从消费者的角度来看,它看起来像这样:

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

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

如果您不喜欢配置太多,请查看 Create-React-App:Create-React-App - Adding Custom Environment Variables。在幕后,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"'
        }
      }
    }),
    ...
  ]
};

使用它没有时髦的环境变量或跨平台问题(使用环境变量)。您所做的只是 运行 正常的 webpackwebpack -p 分别用于开发或生产。

参考:Github issue

你可以传递任何命令行参数无需额外的插件使用--env自webpack 2:

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

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

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

Source

添加到一堆答案中:

使用 ExtendedDefinePlugin 而不是 DefinePlugin

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

ExtendedDefinePlugin 使用起来更简单并且有文档记录:-) link

因为 DefinePlugin 缺乏 好的文档,我想帮忙,说它实际上像 #DEFINE in c# 一样工作。

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

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

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

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

在代码中

使用

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

由于我在 上的编辑未获批准,请发布其他信息。

如果您想从 package.json 中选择值,就像定义的 版本号 并通过 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 的元数据:

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

Thanks to 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 的一些提示:

The DefinePlugin allows you to create global constants which can be configured at compile time. This can be useful for allowing different behavior between development builds and release builds. For example, you might use a global constant to determine whether logging takes place; perhaps you perform logging in your development build but not in the release build. That's the sort of scenario the DefinePlugin facilitates.


这是为了让您检查是否输入 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',
       }),
]

定义插件变量并将其添加到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 文件来保持环境变量干燥。

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)。 Docs here.

我不太喜欢...

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

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

基本上有了这个配置,一旦你编译你的代码,所有进程环境变量将从整个代码中删除,不会有一个 process.env.VAR 由于 babel 插件 transform-inline-environment-variables PS 如果你不想以一大堆未定义结束,请确保在 webpack 调用 babel-loader 之前调用 env.js,这就是为什么它是 webpack 调用的第一件事。 babel.config.js 文件中的变量数组必须与 env.js 上的对象相匹配。现在只有一件事要做。 添加一个 .env 文件将所有环境变量放在那里,该文件必须位于项目的根目录,或者随意将它添加到任何你想要的地方,只需确保在 [=36= 上设置相同的位置] 文件并将其添加到 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

同样的逻辑适用于 React 和所有其他

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

没有插件 troll 的 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种方式只是基本的,我相信还有更多的方式。但是我觉得这4ways可能是最简单的。

我的 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

A secure webpack plugin that supports dotenv and other environment variables and only exposes what you choose and use.

通过一些基于 defaults 选项的配置的解决方法来实现这一点,一旦包有 .env.defaults 文件作为环境变量的初始值,您就可以将它用于 development 和让.env为您制作。

用法

  • 安装包
npm install dotenv-webpack --save-dev
  • 创建 .env.defaults 文件
API_URL='dev_url/api/'
  • 创建一个 .env 文件留空,让 defaults 工作,在部署过程中更新它
  • 配置 webpack - webpack.config.js
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/

dotenv-webpack
dotenv-defaults