react打包配置动态环境变量
前言: 传统的前端项目打包基本都是将环境变量写在项目中再进行编译打包,当一些环境变量需要改变的时只能改动项目再进行打包,比如:三套测试环境,有不同的后端api调用,那么每个环境对应的前端环境变量是不同的,前端开发需要配置不同的环境参数,有没有什么办法像java项目一样,开发人员是不需要知道环境配置数据,只要发布的时候配置对应的环境变量就可以了呢?本文就是解决react项目运行时的环境变量问题。
问题本质
浏览器环境内部是没有环境变量这个东西的,我们现在使用的任何解决方案都不过是虚假的抽象,但是很多文档里面都有提及到.env文件,在代码中使用process.env 就可以使用环境变量了,但实际上process并不存在于浏览器环境中,它只存在于node环境中,webpack打包后process.env都会替换成给定的字符串,这就意味着前端的环境变量只能在构建前(中)配置,一旦构建完成后就无法更改。
可行性方案调研
a.通过接口获取环境变量
通过一个前置接口,在react项目运行时获取所需的环境变量,这样环境变量就可以完全由后端控制,此方案可行,但是有缺点,react项目启动时需要有网络请求,在后端接口不稳定的情况下,整个react项目都无法正常运行。
b. 加载本地js文件
在react项目启动前加载本地js文件,该文件不参与react代码的编译打包,改js文件中存放所有环境变量,当项目编译完成后将改js文件添加进项目,再将该js中的变量分配到全局的window对象属性,项目中只要直接使用window属性即可。此方法稳定,在配置js文件中放入默认环境变量,及时没有配置该js中的环境变量,项目也能够在默认的环境变量下运行。
涉及环境语言
docker/docker-compose
react/js/node
shell/nginx
实现
选取一个react项目,创建.env 文件,并放置在项目根目录,该文件用于存放项目编译前的环境变量,或者编译后的配置变量,文件中都是 key value 形式的参数。ex:
API_URL=http://localhost:8080
将.env的环境变量分配到window属性上面。
新建脚本文件env.sh放到根目录下。
#!/bin/bash
# Recreate config file
rm -rf ./env-config.js
touch ./env-config.js
#!/bin/sh
# line endings must be \n, not \r\n !
echo "window.env = {" > ./env-config.js
awk -F '=' '{ print $1 ": \"" (ENVIRON[$1] ? ENVIRON[$1] : $2) "\"," }' ./.env >> ./env-config.js
echo "}" >> ./env-config.js
上面的脚本就会将.env 的环境变量转化到 env-config.js文件中。
将env-config.js加载到项目中,在项目index.html <head>
标签下加载env-config.js文件
`<script type="text/javascript" src="/env-config.js"></script>`,
项目中使用windows属性中的环境变量
const api_url = window?.env?.API_URL
这样的话,在运行时也可以更改api_url,只要刷新界面,api_url值就可以变化。
打包镜像
在根目录下放置打包需要的nginx配置文件
创建好 default.conf
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
expires -1; # Set it to different value depending on your standard requirements
}
}
开启gzip.conf压缩
gzip on;
gzip_http_version 1.0;
gzip_comp_level 5; # 1-9
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
# MIME-types
gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
text/plain
text/x-component;
在根目录下创建 Dockerfile
# => Build container,select node version
FROM node:16.16.0 as builder
WORKDIR /app
COPY package.json .
#COPY yarn.lock .
RUN npm config set registry https://registry.npm.taobao.org -g
RUN npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
# RUN npm
COPY . .
RUN npm install --unsafe-perm=true
RUN npm run build
# => Run container
FROM nginx:1.15.2-alpine
# Nginx config
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx
# Static build
COPY --from=builder /app/dist /usr/share/nginx/html/
# Default port exposure
EXPOSE 80
# Copy .env file and shell script to container
WORKDIR /usr/share/nginx/html
COPY ./env.sh .
COPY .env .
# Add bash
RUN echo http://mirrors.aliyun.com/alpine/v3.7/main/ >> /etc/apk/repositories
RUN apk add --no-cache bash
# Make our shell script executable
RUN chmod +x env.sh
# Start Nginx server
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]
# CMD ["/bin/bash", "-c", "nginx -g \"daemon off;\""]
动态配置
使用docker run 直接运行打好的镜像,使用-e `` 环境参数,也可以使用docker-compose 来运行镜像
ex:
version: "3.2"
services:
test-react-app:
image: test-react-app
ports:
- "3000:80"
environment:
- "API_URL=http://[ip]:8080"
当我们使用k8s部署时,可以直接将我们需要的.env文件挂在到容器中
/usr/share/nginx/html/.env
中,重启项目就会动态替换了。