现在 deployment 一般都是 docker 或者类似系统
在生产环境之外,都会有不同的测试环境,比如 staging,qa 等
假设 build 好的 image 都是一样的,你只要通过设置不同的当前 environment ,app 就会读取对应的环境文件
比如在 rails 下,设置当前 environment = staging,rails 就会读取.env.staging + .env
当前 environment = production,rails 就会读取.env.production + .env
但是 react 太怪了,只有 3 种环境,而且你不能自己修改 NODE_ENV
我看了好多种办法,比较经典比如 www.codingdeft.com/posts/react-environment-variables/,利用 env-cmd 读取一些 custom env file
但是 env-cmd 有个大问题,这是在 build image 阶段执行的,本来一模一样的 image ,仅仅是环境文件的不同
我要生成不同的 image 吗?比如弄个 staging image ,跑 yun run build-staging; qa image 运行 yun run build-qa ?
本来明明只要一个 image ,pass 不同的当前 env 过去就可以了。另一种奇淫技巧是,在一个 image 内生产不同 build 放在不同的 folder 下,然后制定读取不同 folder ,这个办法被我们 ops team 骂半死
我想因为各种环境都会读取.env ,有没有办法在 deploy image 前,弄个脚本,比如当前是 staging env ,执行 .env.staging >> .env
这样可行吗?或者大家有其他好办法?

找到一个非常类似的 stackoverflow.com/a/53228931/2251303 ,这个人就是把 build 的不同环境,放到一个 image 的不同文件夹下,结果被我们 ops 老大骂死了

我没有其他办法了,谁有招?否则就只能根据不同环境,成生不同的 image ,这个也不好

我试着本地跑了下 yarn run build ,发现 react 把环境变量直接在生产文件中替换了,是 hard code 进去的
所以一旦 yarn run build 跑完,你再修改.env 都是没用的
不像其他 app framework ,变量文件是运行时动态加载的

我想不出任何比 build 的不同环境,放到一个 image 的不同文件夹下的办法了

Dockerfile:

ENV NODE_ENV production
ARG FILE
COPY .env ./.env
COPY $FILE ./.env.production

Build 的时候

docker build . --build-arg FILE=.env.staging

在 public 下放置环境 env.json 管理环境变量, 使用 fetch('/env.json') 来加载环境变量, 这样一个镜像只要挂载文件或者 configMap 就能用在多个环境下

大哥 react 是前端框架,构建出来之后是静态文件的。你一个静态文件,自然也不可能运行的时候有变量哇。
解决的办法也有,那就是别把变量编译到代码里,用别的办法识别环境…比如放到 HTML 里,再由 js 读出来。

前端的运行时在浏览器里,.env 只在 build 的时候有用。有多个环境的话,可以考虑后端返回不同的配置来改变前端的状态

上面提的涉及到其他问题,这样把所有的变量都编译到一个文件,生产文件可以看到任何其他环境的变量

总之,按照后台编译,动态加载环境变量是无解了

可以,但是就不能一个 image 在各个环境通用了

staging 环境需要生成 staging image
qa 环境需要生成 qa image
pre-prod 环境需要生成 pre-prod image

这些 image 除了环境变量不同,其他其实都一样

sorry ,我理解你错误了,你意思是前端通过 api 从后端获取环境变量

这样子肯定不行的

是需要区分密钥之类的吗?我能想到需要区分环境的就是密钥了

如果文件完全一样,只有环境变量的值不同,那么你可以在 Dockerfile 中用 ENV 指定环境变量名称,然后通过.env 文件来配置环境变量

另外和你部署的方法也有关系,如果是 docker-compose 部署,除了写在 envfiles 里,你也可以直接通过 environment 来把它配置到 docker-compose.yml 中,如果是 kubernetes 部署也可以配置到 pod 的环境变量中

是的,比如要 call 三方 api ,人家有提供测试环境和生产环境

不管各种花样,react 生成的静态文件是直接把环境变量 hard code 进去的,不是动态加载的

只有生成多个生产文件,才能解决环境变量的问题

如果 image 是一样,那就不是 webpack 里面用到的环境变量,学 4 楼说的那样写个 json 在 public 下,html 直接引,不同的环境用 configmap 挂文件覆盖之类的方法不就能解决了

还有这跟 react 也没关系吧

在命令行中加入 NODE_ENV=xxx 就可以改变 NODE_ENV 。process.env 在前端是通过 webpack DefinePlugin 构建时直接文本替换,在构建代码执行的时候也是存在内存中,肯定不会实时读取 .env 文件

CRA 的话可以使用 REACT_APP_ENV

呃,没看清楚需求 忽略上条回复
可以参考下 create-react-app.dev/docs/adding-custom-environment-variables#what-other-env-files-can-be-used

当初这么搞的一大原因,是 leader 说要隐藏生产文件中在环境变量中的的 password 和 keys

我现在一看,react 生成的生产文件,直接是把环境变量 hard code 进去的,用户一看明明白白,根本没有任何隐藏的必要

迫于上条及上上条都答非所问,午休时间琢磨了下
react app 从 html script 里读取环境变量的配置吧,docker nginx image ( 1.19 版本开始) 支持使用 /etc/nginx/templates/*.template 模板文件来提取环境变量到 /etc/nginx/conf.d 中,所以你可以试试在启动 nginx 时把想要的环境变量动态的通过 nginx http_sub_filter 指令,注入到 index.html 中

#21 我碰到和楼主一样的问题,现方法是 build N 个环境,等我来研究下你说的,多谢~

我也是刚去看了下 nginx image 的文档,发现可以这么搞 差不多就是利用 nginx 来做动态化下发页面配置了吧 😂

我看了下,这个方法也是厉害,code + server 配置

但是还要我之前说的,前端是没有任何秘密的
用户无论如何都是可以看到想看的变量和密码,如果你植入了前端的话

我们老大弄环境变量的一大原因是,想影响前端调用位于环境变量的 password 和 key ,这其实是暴露于用户的

#24 抱歉。我想了想,跟你不一样的情况应该是我只需要在 index.html 有不同的 baseUrl 就可以了,因为到生产环境的时候静态文件会经过 CDN

需求:

  1. 打包一次
  2. 每个环境变量不同

所以打包永远都是 webpac production mode, 环境变量通过 nodejs,通过模版写入全局变量.这样代码就能读取不同环境的变量

#26 如果有 node 层,可以用这个

developers.redhat.com/blog/2021/03/04/making-environment-variables-accessible-in-front-end-containers#rebuild_your_image_and_start_the_server

参考: developers.redhat.com/blog/2021/03/04/making-environment-variables-accessible-in-front-end-containers

在容器启动时,替换环境变量到打包好的 js 文件中

react build 后的文件是无法修改配置的,建议一下两种
1.在 docker build 时指定参数,--build-arg host= 参考 #3
2.利用环境域名不同,react 里加载相对路径的配置文件 /config.js ,实际路径是 stg/config.js 或 pd/config.js

仔细想想,容器注入的环境变量是给谁看的?是容器内启动的进程,前端代码想得到这些变量就必须通过它们来传递

正如 create-react-app 文档中说的那样:WARNING: Do not store any secrets (such as private API keys) in your React app!
API 的密钥应该存在服务器,React app 通过服务端接口再去调用第三方服务
晚上闲着没事,写了篇博客记录一下上面提到的思路,供参考 7anshuai.js.org/blog/add-react-app-env-vars-by-nginx/