在不同环境中运行的 react.js redux 生产版本中将环境变量渲染到浏览器 [英] Rendering an environment variable to the browser in a react.js redux production build running in different environments

查看:14
本文介绍了在不同环境中运行的 react.js redux 生产版本中将环境变量渲染到浏览器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The readme of the react redux realworld.io application at https://github.com/gothinkster/react-redux-realworld-example-app says to edit the src/agent.js to change the API_ROOT to point to a different backend api instance. We want to set things up so that API_ROOT can be defined by an environment variable that is different within the multiple environments (e.g., "staging" and "live") where we run the production build.

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

We are running in containers on openshift kubernetes following 12factor.net principles where the code is built once then promoted through environments. We can spin up new environments with a single command so we don’t want to have a switch statement within the code that names each environment and hardcodes the backend API_ROOT for each environment. Instead, I want to be able to run an existing production build container image in a fresh environment using an environment variable change the API_ROOT to point to the correct backend API we want to test against.

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

I have looked at a number of different blogs, stackoverflow answers and the official documentation. The main problem is that typical solutions "bake in" the process.env.API_ROOT environment variable at build time else have a switch that hardcodes the details of all environments into the code. Neither of which are satisfactory as we want to able to take the latest stable code in an existing container and run it in a new environment using the API running there.

到目前为止,我最接近的是编辑代码以将 process.env.API_ROOT 呈现为 <script> 标记,该标记将其设置在 <代码>window.API_ROOT 变量.然后检查是否存在,否则在为 API_ROOT 定义 const 时使用默认值.这感觉非常具有侵入性且有点脆弱,我不清楚在 https://github.com/gothinkster/react-redux-realworld-example-app

The closest I have got so far is to edit the code to render the process.env.API_ROOT into a <script> tag that sets it on a window.API_ROOT variable. Then check whether that exists else use a default when defining the const for API_ROOT. This feels very invasive and a bit fragile and it is not clear to me where is the best place to render such a script tag in the sample app at https://github.com/gothinkster/react-redux-realworld-example-app

推荐答案

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

Issue #578 of react-create-app has a good answer. tibdex suggested using a public/env.js that is generated with the correct properties then in the index.html add:

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

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

That env.js script can set the API_ROOT on the window:

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

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

And agent.js can check for the window.env.API_ROOT else default:

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 命令生成它.

Exactly how that file is created from an environment variable he doesn't describe but I was able to have the npm start command generate it.

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

Moorman then suggested simply writing an express server that serves that /env.js else 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+"'}
";

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 中的启动脚本很简单:

To get that to work the start script in the package.json is simply:

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

然后一切正常.如果 API_ROOT 在环境变量中定义,那么 server.js 将在 window.envagent.js 上生成它代码> 将使用它.

Then everything works. If API_ROOT is defined in environment variables then the server.js will generate it on window.env and the agent.js will use it.

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

update I set a cache time of five minutes on env.js with res.setHeader("Cache-Control", "public, max-age=300"); as the setting is rarely going to change.

更新 我读到了很多关于这个主题的困惑,人们按照更改您的工作流程以与工具的默认值保持一致"的方式回答它.12-factor 的想法是使用被确立为工具应遵循的最佳实践的工作流,反之亦然.具体来说,标记的生产就绪容器应该可以通过环境变量进行配置并通过环境进行提升.然后它是相同的东西",经过调试和测试,可以实时运行.在这种单页应用程序的情况下,它需要浏览器访问服务器以加载环境变量,而不是将它们烘焙到应用程序中.恕我直言,这个答案是一种直接而简单的方法,可以遵循 12 因素最佳实践.

update I read a lot of confusion around this topic and people answering it along the lines of "change your workflow to align to the defaults of the tools". The idea of 12-factor is to use a workflow that is established as best practice that the tools should follow, not vice-versa. Specifically a tagged production ready container should be configurable by environment variables and promoted through environments. Then it's "the same thing" that is debugged and tested that runs in live. In this case of a single page app it requires that the browser makes a trip to the server to load the environment variables rather than baking them into the app. IMHO this answer is a straightforward and simple way of doing that to be able to follow 12-factor best practices.

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

update: @mikesparr gives a good answer to this problem at https://github.com/facebook/create-react-app/issues/982#issuecomment-393601963 which is to restructure the package.json to do the webapp work of generating the SPA upon start up. We took this approach as a tactical workaround. We are using a saas openshift kubernetes that charges for memory. Building our react app with webpack needs 1.2Gb (and rising!) So this approach of moving the npm build to the container startup command we need to allocate 1.2Gb to every pod we start which is a significant amount of additional costs for a single page app whereas we can get away with 128MB as the memory allocation when the app is precompiled. The webpack step is also slow as it is a large app. Building every time we start up the app slows down rolling deployments by many minutes. If a VM crashes and kubernetes starts replacement containers on a new VM it takes minutes to start up. A precompiled app starts in a few seconds. So the solution of "webpack at startup" is not satisfactory in terms of resource consumption and speed for real business application that are tens of thousands of lines of code. IMHO this answer of fetching a configuration script from the server is superior.

这篇关于在不同环境中运行的 react.js redux 生产版本中将环境变量渲染到浏览器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆