问题描述
react redux realworld.io应用程序的自述文件,位于 https://github .com/gothinkster/react-redux-realworld-example-app 表示要编辑src/agent.js
以更改API_ROOT
指向其他后端api实例.我们想要进行设置,以便可以由运行生产版本的多个环境(例如,"staging"和"live")中不同的环境变量定义API_ROOT
.
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.
我们正在遵循12factor.net原则在openshift kubernetes上的容器中运行,在该容器中先构建代码,然后通过环境对其进行推广.我们可以使用一个命令来启动新环境,因此我们不想在代码中使用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
推荐答案
问题# 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
其他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+"'}\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
中的启动脚本很简单:
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.env
上生成它,而agent.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.
更新我使用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要素的想法是使用作为工具应遵循的最佳实践而建立的工作流,反之亦然.特别是,带标签的生产就绪容器应该可以通过环境变量进行配置,并通过环境进行升级.然后,经过调试和测试的同一件事"就可以实时运行.在单页面应用程序的情况下,它要求浏览器访问服务器以加载环境变量,而不是将其烘焙到应用程序中.恕我直言,这个答案是一种简单易行的方法,可以遵循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以完成生成启动时会显示SPA.我们将此方法用作战术上的解决方法.我们正在使用saas openshift kubernetes来收取内存费用.使用webpack构建我们的React应用需要1.2Gb(并且还在不断增加!),因此将npm构建移到容器启动命令的这种方法需要为我们启动的每个Pod分配1.2Gb,这对于单个页面来说是大量的额外费用应用程序,而在预编译应用程序时,我们可以获得128MB的内存分配. Webpack步骤很慢,因为它是一个大型应用程序.每次启动应用程序时构建,都会使滚动部署速度降低几分钟.如果VM崩溃并且kubernetes在新VM上启动替换容器,则需要几分钟的时间来启动.预编译的应用程序将在几秒钟内启动.因此,在成千上万行代码的实际业务应用程序的资源消耗和速度方面,启动时的webpack"解决方案并不令人满意.恕我直言,从服务器获取配置脚本的答案很优越.
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生产构建中将环境变量呈现给浏览器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!