如何将.env文件中的变量传递给webpack配置?

68

我对webpack比较新,几乎已经掌握了所有的构建部分,但现在遇到一个问题,我想从.env文件中传递环境变量到webpack配置中,以便我可以通过webpack.DefinePlugin插件将该变量传递给我的构建文件。

目前我能够直接从webpack传递环境变量到我的构建中。请参阅下面我在webpack中使用的代码。

new webpack.DefinePlugin({
            "API_URL": JSON.stringify("http://my-api.com"),
            "FRONT_END_API_KEY" : "MYFRONTENDKEYGOESHERE"
        }),

我的package.json的构建脚本是

"scripts": {
    "start": "NODE_ENV=development webpack-dev-server --progress --port 8000 --content-base app/build/src"
    } 

1
真的吗?难道没有人会解决这里明显的问题吗?如果你将一个秘密传递给DefinePlugins,那么它就不再是秘密了!!!只传递你可以公开的环境变量。 - NSjonas
@NSjonas 前端环境中不应定义任何安全配置值。前端使用的配置值可以被任何人拦截,无论您将其保存在客户端的哪个位置。如果您将其保存在客户端,则任何具有基本编程技能的人都可以找到它。此外,不应在前端 .env 中保存任何安全密钥,这必须在服务器端 ENV 中处理。现在,所有现代应用程序解决方案都会分别提供前端和后端的密钥,并进行域验证等操作。例如:pusher、send-bird 等。 如果我理解有误,请纠正我。 - Rameez Rami
是的...我的观点是,这些解决方案都没有指出作者(以及下面的其他人)试图将秘密传递到一个Angular应用程序中! - NSjonas
2
我已经更新了问题。整个问题的目的是从.env文件中获取一些值。没有人会在前端或前端的.env文件中保存 Secret API。我的所有密钥都在后端的.env文件中。 - ninja dev
9个回答

130

你可以使用dotenv软件包来实现此目的。

npm install dotenv --save

安装完包之后,在你的配置文件顶部添加以下内容:
const webpack = require('webpack'); // only add this if you don't have yet

// replace accordingly './.env' with the path of your .env file 
require('dotenv').config({ path: './.env' }); 

接着在plugins部分中添加如下内容:

new webpack.DefinePlugin({
  "process.env": JSON.stringify(process.env),
}),

28
为了让这个工作起来,我不得不将解析后的 dotenv 转换成一个有效的 JSON 字符串,方法是使用 JSON.stringify(dotenv.parsed) - Sanderfish
1
你也可以更具体地做 "process.env.YOUR_VARIABLE": dotenv.parsed.YOUR_VARIABLE",以避免处理整个对象。在 webpack.config.js 中,我只需要一个变量。 - nicholascm
3
该行代码"process.env": JSON.stringify(dotenv.parsed)仅传递在.env文件中定义的变量,会丢失在shell中设置的所有环境变量。我必须调用dotenv并传递实际的process.env,例如: "process.env": JSON.stringify(process.env) - Young Suk Ahn
1
@RameezRami,你不理解。你的秘密不在.env文件中。这个文件只是将现有环境变量添加到你已经在操作系统中拥有的环境变量中。如果你只是本地构建,那可能没问题,但是如果你有一个工作,例如每当你推送分支时触发CI构建的工作,那么该环境肯定包含秘密信息,如果你执行JSON.stringify(process.env),它们将被暴露出来。 - Chololoco
1
同意@Chololoco的说法,这确实很危险。它会捆绑操作系统上的所有环境变量(不仅仅是.env文件),这些变量可能包含各种机密信息。它能工作,但我建议将其远离任何非本地环境。 - garryp
显示剩余12条评论

24

webpack + dotenv

我从接受的答案中得到了灵感,但它对我不起作用。可能是dotenv的API已经改变了。

以下内容对我有效:

import dotenv from 'dotenv'
import { DefinePlugin } from 'webpack'


...

plugins: [
    new DefinePlugin({
      'process.env': JSON.stringify(dotenv.config().parsed)
    })
]

...

1
@TSR 是的。https://github.com/motdotla/dotenv/blob/7301ac9be0b2c766f865bbe24280bf82586d25aa/lib/main.js#L77 - Tyler Liu
这将通过在部署时使用实际的环境变量来绕过 .env 的功能。 - Liam
@Liam webpack主要用于将JS代码捆绑在一起以在浏览器中运行。而且在浏览器中,您无法访问实际的环境变量。因此,我认为env vars主要用于捆绑时间。 - Tyler Liu
也许是10年前,但现在肯定不是了。Node.js已经存在了相当长的一段时间。 - Liam
@Liam 为了避免覆盖实际环境变量,只需将 'process.env' 重命名为其他名称,例如 "bundled.env"。然后使用 "bundled.env" 来处理捆绑的环境变量,而使用 "process.env" 来处理实际环境变量。 - Tyler Liu
显示剩余2条评论

12

虽然只部分符合你的需求,但我发现这个公式对我来说运作最佳。

我使用了两个库的组合:dotenv 用于读取webpack.config.js(配置)所需的.env文件,并且webpack-dotenv-plugin 用于验证(基于.env.example文件)和所有变量从.env文件传递到应用程序代码:

我的webpack.config.js部分内容:

// this is to load env vars for this config
require('dotenv').config({ // it puts the content to the "process.env" var. System vars are taking precedence
    path: '.env.webpack',
});
// and this to pass env vars to the JS application
const DotenvPlugin = require('webpack-dotenv-plugin');

插件部分:

plugins: [
    // ...
    new DotenvPlugin({ // makes vars available to the application js code
        path: '.env.webpack',
        sample: '.env.webpack.example',
        allowEmptyValues: true,
    }),
    // ...
]

9
我找到的最简单的解决方案是使用这个npm包: dotenv-webpack 创建一个.env文件
// .env
DB_HOST=127.0.0.1
DB_PASS=foobar
S3_API=mysecretkey

把它添加到你的Webpack配置文件中

// webpack.config.js
const Dotenv = require('dotenv-webpack');

module.exports = {
...
plugins: [
new Dotenv()
]
...
};

在你的代码中使用

// file1.js
console.log(process.env.DB_HOST);
// '127.0.0.1'
Resulting bundle
// bundle.js
console.log('127.0.0.1');

5
很抱歉,dotenv-webpack插件不能将那些变量传递到webpack.config.js文件,只能传递给生成环境代码。不过我有这个相同的设置。 - Blaiz
它在我这里是正常工作的,伙计,看起来你漏掉了什么。 - Mahmoud Abd AL Kareem
3
不是这样的。如果您正试图直接在webpack.js文件中使用这些变量。 - ripreal
这是一个不同的问题。这个插件不允许你在webpack.config中使用dotenv,而这正是问题所询问的内容。 - Liam

5

很抱歉我无法发表评论以澄清任何信息。

你可以这样做:

var env = require('.env');

然后
new webpack.DefinePlugin({
            "API_URL": JSON.stringify("http://my-api.com"),
            "SECRET_KEY" : "MYSECRETKEYGOESHERE",
            "env_property": env.property
        }),

但是,我在回答中对你的.env文件和设置方式做了一些假设。

1
为什么要将 API_URL 转换为字符串?为什么不把 SECRET_KEY 也转换成字符串? - Matteo Gaggiano
你能详细说明一下你所做的假设吗?这个解决方案看起来很有趣,因为你在webpack.config文件中暴露了变量的值。 - jwknz

5

首先...

看起来你正在尝试将秘密信息传递到一个angular应用程序中。

在客户端(浏览器)JavaScript中,不存在所谓的“秘密”!!!

任何传递给DefinePlugin的内容都可以轻松提取。

既然我们已经搞清楚了这一点....

Webpack现在有了环境插件,这使得将env变量传递到GlobalDefine插件中更加容易。 从文档中可以看到:

new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG']);

这相当于以下的 DefinePlugin 应用程序:
new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
  'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
});

如果你正在使用 dotenv 来管理环境变量,你可以使用 dotenv webpack 插件。它只会包含在你的代码中引用的变量,因此只要你不引用你的机密变量,它们就不会被包含进去。

没错。我对此毫不怀疑。前面给出的任何内容都可以被提取。整个问题是为了从.env文件中获取变量的想法。我将保存MAP_FRONT_END_KEY、ALGOLIA_SEARCH_KEY等类似的前端API密钥。 - ninja dev
我看到你更新了问题。在此之前,它似乎让人觉得你试图传递一个真正的秘密。 - NSjonas
兄弟,这只是一段示例文本。我在输入BLA_BLA时,将其替换成了YOUR_SECRETKEY_GOES_HERE。问题的重点是从.env中获取值。 - ninja dev
兄弟,变量名很重要,特别是在上下文有限的“示例”代码中。如果你写了 "SECRET_KEY" : "MYSECRETKEYGOESHERE",我当然会认为你认为这个值应该保密。人们在他们的SPA中包含秘密信息是一个严重的问题,所以我会保留我的答案的前半部分。感谢你更新问题,这样其他人就不会认为这样做是可以的了。 - NSjonas

2

来自 webpack 文档

webpack 命令行环境选项 --env 允许您传递任意数量的环境变量。环境变量将在您的 webpack.config.js 中可用。例如,--env.production 或 --env.NODE_ENV=local(NODE_ENV 通常用于定义环境类型,请参见此处)。

在您的 package.json 中:

webpack --env.NODE_ENV=local --env.production --progress

在你的webpack.config.js文件中

module.exports = env => {
  // Use env.<YOUR VARIABLE> here:
  console.log('NODE_ENV: ', env.NODE_ENV) // 'local'
  console.log('Production: ', env.production) // true

  return {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    }

原始问题是关于存储在 .env 文件中的配置变量,而不是通过 CLI 传递,所以据我所知这样做行不通。 - Blaiz

0
dotenv-flow-webpack可以为您完成这项任务。
它是一个webpack插件,允许您在JavaScript Web应用程序中安全地使用环境变量,并使用dotenv-flow.env*文件加载策略加载它们。

dotenv-flow扩展了dotenv,增加了对特定NODE_ENV.env*文件的支持,例如.env.development.env.test.env.stage.env.production,以及适当的.env*.local覆盖,允许您的应用程序具有多个环境,并根据当前的NODE_ENV动态加载它们。

受到CreateReactApp中将配置存储在.env*文件中的方法的启发, 以及Twelve-Factor App方法论的一般性影响,尤其是其中关于将配置存储在环境中的部分。

0
在我的情况下,我有许多环境需要支持,例如:.env.local.env.dev等等。我已经解决了将特定环境的环境文件加载到webpack配置中的问题,具体步骤如下: 步骤1:为特定环境创建一个文件 - 例如:.env.local
API_HOST=127.0.0.1
API_KEY=mysecretkey
...

第二步:创建一个适用于所有环境的webpack配置文件 - webpack.common.js
...
...
const webpack = require('webpack')
const dotenv = require('dotenv')
...
...

module.exports = env => {
    return {
        resolve: {...},
        devServer: {...},
        module: {...},
        plugins: [
            new webpack.DefinePlugin(
               {'process.env': JSON.stringify(dotenv.config(path: env.ENV_FILE}).parsed)})
  }
}

这里需要注意的重要事项是,module.exports应该返回一个函数而不是一个对象,这样它才能访问由环境特定的webpack配置文件传递的环境变量(例如这个例子中的ENV_FILE),比如webpack.local.js(步骤3)。 步骤3:创建一个环境特定的webpack配置文件 - webpack.local.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = env => merge(common(env), {
    mode: 'development',
    output: {
        publicPath: 'http://localhost:3000',
    },
    devtool: 'inline-source-map',
    devServer: {
        watchFiles: ['src/**/*'],
        hot: true,
        port: 3000
    },
})

在这个特定环境的文件中,webpack.common.js文件被导入为一个函数(const common),该函数在webpack merge的过程中使用env参数进行调用。这里的env参数是通过package.json中webpack命令的--env选项填充的(步骤4)。
第4步:在package.json中设置webpack命令。
"scripts": {
  "start": "webpack serve --open --env ENV_FILE=.env.local --config webpack.local.js",
  "build:prod": "webpack --env ENV_FILE=.env.prod --config webpack.prod.js"
  ...
}

这里的npm命令`start`会使用`webpack.local.js`作为webpack配置来启动一个本地web服务器。当调用进入`webpack.local.js`时,它会将`ENV_FILE`键填充到由`module.exports`返回的函数的`env`参数中。
类似地,您可以设置`webpack.prod.js`,它可以根据上述`webpack.local.js`的示例来针对您的生产环境进行特定配置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接