9

https://github.com/gothinkster/react-redux-realworld-example-app上的 react redux realworld.io 应用程序的自述文件说要编辑src/agent.js以更改API_ROOT指向不同的后端 api 实例。我们希望进行设置,以便API_ROOT可以通过在我们运行生产构建的多个环境(例如,“staging”和“live”)中不同的环境变量来定义。

我们遵循 12factor.net 原则在 openshift kubernetes 上的容器中运行,其中代码构建一次,然后通过环境进行升级。我们可以使用单个命令启动新环境,因此我们不希望在代码中使用 switch 语句来命名每个环境并API_ROOT为每个环境硬编码后端。相反,我希望能够在新环境中运行现有的生产构建容器映像,使用环境变量更改API_ROOT指向我们要测试的正确后端 API。

我查看了许多不同的博客、stackoverflow 答案和官方文档。主要问题是典型的解决方案process.env.API_ROOT在构建时“烘焙”环境变量,否则有一个开关,将所有环境的细节硬编码到代码中。两者都不令人满意,因为我们希望能够在现有容器中获取最新的稳定代码,并使用在那里运行的 API 在新环境中运行它。

到目前为止,我最接近的是编辑代码以将其呈现process.env.API_ROOT为将<script>其设置在window.API_ROOT变量上的标签。然后检查是否存在,否则在为 API_ROOT 定义 const 时使用默认值。这感觉非常具有侵入性并且有点脆弱,我不清楚在https://github.com/gothinkster/react-redux-realworld-example-app的示例应用程序中呈现这样一个脚本标签的最佳位置是哪里

4

3 回答 3

16

react-create-app 的Issue #578有一个很好的答案。tibdex建议使用使用public/env.js正确属性生成的 a ,然后index.html添加:

 <script src="%PUBLIC_URL%/env.js"></script>

env.js脚本可以在窗口上设置 API_ROOT:

window.env={'API_ROOT':'https://conduit.productionready.io/api'}

并且agent.js可以检查window.env.API_ROOTelse 默认值:

function apiRoot() {
  if( window.env.API_ROOT !== 'undefined') {
    return window.env.API_ROOT
  }
  else {
    return 'https://conduit.productionready.io/api'
  }
}

const API_ROOT = apiRoot();

该文件是如何从他没有描述的环境变量中创建的,但我能够让npm start命令生成它。

Moorman然后建议简单地编写一个服务于/env.jselse的快速服务器index.html

const express = require('express');
const path = require('path');

const app = express();

app.use(express.static(path.join(__dirname, 'build')));

const WINDOW_ENV = "window.env={'API_ROOT':'"+process.env.API_ROOT+"'}\n";

app.get('/env.js', function (req, res) {
  res.set('Content-Type', 'application/javascript');
  res.send(WINDOW_ENV);
});

app.get('/*', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(process.env.PORT);

为了让它工作,启动脚本package.json很简单:

"start": "PORT=8080 node server.js",

然后一切正常。如果API_ROOT在环境变量中定义,server.js则将生成它window.envagent.js使用它。

更新我在 env.js 上设置了五分钟的缓存时间, res.setHeader("Cache-Control", "public, max-age=300");因为该设置很少会改变。

更新我读到了很多关于这个主题的困惑,人们的回答是“改变你的工作流程以适应工具的默认设置”。12 因素的想法是使用已确立为工具应遵循的最佳实践的工作流程,反之亦然。具体来说,标记的生产就绪容器应该可以通过环境变量进行配置并通过环境进行升级。然后它是经过调试和测试的“同一件事”,可以在现场运行。在这种单页应用程序的情况下,它要求浏览器访问服务器以加载环境变量,而不是将它们烘焙到应用程序中。恕我直言,这个答案是一种直接而简单的方法,可以遵循 12 因素最佳实践。

更新:@mikesparr 在 https://github.com/facebook/create-react-app/issues/982#issuecomment-393601963为这个问题提供了一个很好的答案这是重组 package.json 以在启动时执行生成 SPA 的 webapp 工作。我们将这种方法作为一种战术解决方法。我们正在使用对内存收费的 saas openshift kubernetes。使用 webpack 构建我们的 react 应用程序需要 1.2Gb(并且还在增加!)因此,这种将 npm 构建移动到容器启动命令的方法我们需要为我们启动的每个 pod 分配 1.2Gb,这对于单个页面来说是大量的额外成本应用程序,而我们可以在应用程序预编译时分配 128MB 作为内存分配。webpack 步骤也很慢,因为它是一个大型应用程序。每次我们启动应用程序时构建都会使滚动部署减慢几分钟。如果 VM 崩溃并且 kubernetes 在新 VM 上启动替换容器,则需要几分钟才能启动。预编译的应用程序会在几秒钟内启动。所以“webpack at startup”的解决方案对于几万行代码的真实业务应用来说,在资源消耗和速度上都不尽如人意。恕我直言,这个从服务器获取配置脚本的答案是优越的。

于 2018-04-23T21:08:45.817 回答
3

您可以直接在 index.html 文件中替换环境变量,从而公开一个全局 ENV 变量。该替换需要在运行时完成,以确保您拥有可以在不同环境中运行的可移植映像。

我在这里创建了一个示例存储库https://github.com/axelhzf/create-react-app-docker-environment-variables

于 2019-01-12T19:18:17.547 回答
2

看看不可变的 Web 应用程序

index.html这是一种在所有其他静态资产之间创建关注点分离的方法:

  1. 它被视为index.html包含所有特定于环境的值的部署清单。

这类似于接受的答案,将环境变量直接包含在index.html

window.env={'API_ROOT':'https://conduit.productionready.io/api'}

它还要求对其他静态资产的引用是唯一的和版本化的。

  1. 它将 javascript 包视为构建一次、发布一次并在多个环境中使用的不可变资产。允许资产在不被修改或移动的情况下通过环境提升到生产环境。

它尊重 12factor 的“构建、发布、运行”和“配置”原则。

这种方法的一大好处是它可以通过简单的发布来实现原子实时发布index.html

于 2019-01-07T15:19:25.157 回答