在不同环境中运行的react.js redux生成版本中将环境变量呈现给浏览器

sim*_*905 4 12factor reactjs react-redux

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

sim*_*905 11

react-create-app的问题#578有一个很好的答案.tibdex建议使用使用public/env.js正确属性生成的,然后在index.htmladd中:

 <script src="%PUBLIC_URL%/env.js"></script>
Run Code Online (Sandbox Code Playgroud)

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

window.env={'API_ROOT':'https://conduit.productionready.io/api'}
Run Code Online (Sandbox Code Playgroud)

并且agent.js可以检查window.env.API_ROOT其他默认值:

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

const API_ROOT = apiRoot();
Run Code Online (Sandbox Code Playgroud)

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

然后Moorman建议简单地编写一个服务于/env.js其他服务器的快速服务器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);
Run Code Online (Sandbox Code Playgroud)

要使其工作,启动脚本package.json就是:

"start": "PORT=8080 node server.js",
Run Code Online (Sandbox Code Playgroud)

一切正常.如果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来做webapp的生成工作SPA启动时.我们采用这种方法作为战术解决方法.我们正在使用saas openshift kubernetes为记忆收费.使用webpack构建我们的react应用程序需要1Gb(并且还在上升!)所以这种将npm构建移动到容器启动命令的方法我们需要为每个启动的pod分配1Gb,这对于单个页面应用程序来说是一个相当大的额外成本,而当预编译应用程序时,我们可以使用128MB作为内存分配.webpack步骤也很慢,因为它是一个大型应用程序.每次启动应用程序时构建都会使滚动部署减慢许多分钟.如果VM崩溃并且kubernetes在新VM上启动替换容器,则启动需要几分钟.预编译的应用程序会在几秒钟内启动.因此,"webpack at startup"的解决方案在资源消耗和速度方面远远不能令人满意.恕我直言这个从服务器获取配置脚本的答案是优越的.

  • "容器中标记的生产构建应该可以通过环境变量进行配置" - 我和你在一起; 我在这个问题上也留下了一些评论,因为我不认为很多人部署React了解是什么让12fa如此强大.我们不应该在其中运行带有Node.js的容器并在容器启动时编译东西,我们也不应该为不同的环境构建多个容器映像! (2认同)
  • @geerlingguy 我真的很惊讶这方面的混乱程度,而且没有标准的样板或插件。当我们在多个服务中部署多种语言时,看到一个框架违反最佳实践是一个明显的缺陷。大多数人似乎很乐意在启动时应用 compile 的工作。然而对我们来说,这意味着我们的单页应用程序需要 1G,我们可以在这么多内存中以不同的语言运行四个后端服务器端应用程序。 (2认同)
  • 是的,我只是在我们的基础设施上计时,并使用生产工件和 `nginx-alpine` 容器我可以在大约 120 毫秒内启动一个 20 MB 的容器,或者我可以使用“启动时构建”技术和容器大小约为 200 MB,启动需要 63,000 毫秒。并在整个启动时间使用 100% 的分配 CPU!绝对是 React 生态系统中的一个主要差距,IMO。 (2认同)
  • 我刚刚发布了一篇关于这个主题的博客文章,因为我知道我会再次遇到这个问题,我会忘记我花了 5 个多小时试图找到任何官方记录的方法:[部署一个 React 单一-page Web 应用程序到 Kubernetes](https://www.jeffgeerling.com/blog/2018/deploying-react-single-page-web-app-kubernetes)。感谢您在这里和 GitHub 上的帖子! (2认同)

axe*_*hzf 6

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

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