通过webpack更改硬编码的url常量以适应不同的环境

43

我有一个 ApiCaller.js 模块,用于向我们的api服务器发起数据请求。它有一个常量字段 API_URL,指向服务器url。

API_URL 常量会因为 devprod 环境而改变。

所以当我需要部署到 dev 环境时,我需要手动更改该 url(API_URL),使其指向 dev-api-server,反之亦然。

我希望这些配置参数在代码外,并且在构建过程中我想要动态更改它们,以便使用不同的设置进行构建。

我正在使用 webpack 来捆绑我的javascript、html和css文件。


你想在 ApiCaller 中暴露 API_URL(你可以编辑 ApiCaller),还是想要更改硬编码的 API_URL(你无法编辑 ApiCaller)? - Everettss
你已经在编辑过的答案中得到了它。没有什么特别的-只是普通的JS。 - Everettss
2个回答

53
你可以将API_URL存储在webpack配置中:
// this config can be in webpack.config.js or other file with constants
var API_URL = {
    production: JSON.stringify('prod-url'),
    development: JSON.stringify('dev-url')
}

// check environment mode
var environment = process.env.NODE_ENV === 'production' ? 'production' : 'development';

// webpack config
module.exports = {
    // ...
    plugins: [
        new webpack.DefinePlugin({
            'API_URL': API_URL[environment]
        })
    ],
    // ...
}

现在您可以在您的 ApiCaller 中使用定义变量 API_URL,它将根据 process.env.NODE_ENV 的不同而有所不同。
ajax(API_URL).then(/*...*/);

(编辑) 如果我有多个生产/开发配置用于不同的环境常量怎么办?

假设您有像上面答案中的 API_URL 一样的变量,还有 API_URL_2API_URL_3,它们应该支持不同的环境设置 production/development/test

var API_URL = {
    production: JSON.stringify('prod-url'),
    development: JSON.stringify('dev-url')
};

var API_URL_2 = {
    production: JSON.stringify('prod-url-2'),
    development: JSON.stringify('dev-url-2'),
    test: JSON.stringify('test-url-2')
};

var API_URL_3 = {
    production: JSON.stringify('prod-url-3'),
    development: JSON.stringify('dev-url-3'),
    test: JSON.stringify('test-url-3')
};

// get available environment setting
var environment = function () {
     switch(process.env.NODE_ENV) {
         case 'production':
             return 'production';
         case 'development':
             return 'development';
         case 'test':
             return 'test';
         default:                // in case ...
             return 'production';
     };
};

// default map for supported all production/development/test settings
var mapEnvToSettings = function (settingsConsts) {
     return settingsConsts[environment()];
};

// special map for not supported all production/development/test settings
var mapAPI_URLtoSettings = function () {
     switch(environment()) {
         case 'production':
             return API_URL.production;
         case 'development':
             return API_URL.development;
         case 'test':                    // don't have special test case
             return API_URL.development;
     };
};

// webpack config
module.exports = {
    // ...
    plugins: [
        new webpack.DefinePlugin({
            'API_URL': mapAPI_URLtoSettings(),
            'API_URL_2': mapEnvToSettings(API_URL_2),
            'API_URL_3': mapEnvToSettings(API_URL_3)
        })
    ],
    // ...
}

(编辑 2)

  1. 如果您将字符串作为环境常量传递,应使用JSON.stringify
  2. 您不需要多次定义new webpack.DefinePlugin。您可以在一个对象中传递给new webpack.DefinePlugin - 这看起来更干净。

问题在于,如果您将这些URL值存储在文件中(而不是webpack.config),那么它们将捆绑到您的应用程序中,并且支持人员或任何有权访问生产环境的人都无法修改这些值,除非需要进行代码更改,这正是首先将配置文件与应用程序代码分离的整个目的。如果您将它们放在webpack.config中,则会遇到一个问题,即每当您进行新部署时,都会覆盖支持已在prod服务器上更新的内容。 - muzurB
@muzurBurcu 关于维护应用程序的分发版本的能力的讨论是与此问题不同的话题。但你是对的:你应该始终创建易于完成并记录的应用程序构建方式。这些天,无论是开发阶段还是支持阶段,npm run:build ... 或类似命令 都是开发应用程序的必要步骤。 - Everettss
我该如何将process.env.NODE_ENV注入为如dev和products之类的某个值?它总是未定义。 - inherithandle
当我尝试使用这个实现时,我无法使用变量 API_URL。ES-lint阻止了webpack构建的完成,所以我不得不包含一行 // eslint-disable-next-line。现在它像该注释下面的任何变量一样工作。 - GoreDefex

2

您可以设置定义插件来定义一个PRODUCTION变量,如下所示(或者如果您使用不同的配置文件进行构建,则可以选择将其设置为true):

new webpack.DefinePlugin({
    PRODUCTION: process.env.NODE_ENV === 'production'
})

然后在你的代码中,你会写入类似以下的内容:

var API_URL = PRODUCTION ? 'my-production-url' : 'my-development-url';

在编译过程中,webpack会将PRODUCTION替换为其值(即truefalse),这将允许UglifyJS对我们的表达式进行缩小:

var API_URL = <true/false> ? 'my-production-url' : 'my-development-url';

最糟糕的情况是uglify无法压缩条件表达式,将其保留原样。

这似乎是一个简约而可行的解决方案,如果配置增加了,有任何更新吗? - Capuchin

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