前端如何获取到宿主机的环境变量?

1. 前言

2020.6.10 日我写了这样的一篇文章:

Vue项目如何获取Docker的环境变量——最小完整实践

里面简述了为什么要做这样一件事情,简单来说就是: 企业开发时多环境下需要前端页面通过获取宿主机的环境变量来动态的设置某个参数。

在上面这篇博客中,我利用 docker 在构建镜像时获取系统环境变量来注入一个属性给 html 文件,然后获取这个对应的属性,挂载到 vue 或者全局即可。但是正如我在文末中写到,我并不是很喜欢这种方式,不优雅也不正式,同时文末我给提出了另一种解决之法。

我思考和实践出了一种全新的方法,极度优雅和简单。

2. 利用 docker 构建镜像时替换变量

可能看标题,会觉得这跟第一种几乎相同,但是,这里的思想是不同的。
主要的区别在于一个是注入变量,一个是替换,一个是挂载变量然后获取,一个是直接获得,极度优雅。

在此记录整个思考和实现的过程。

2.1 思维转换

最开始是怎么思考的呢?

  1. JS 能不能直接获取到环境变量,发现不行,因为 JS 是运行在解释器里面的。
  2. 能不能有一个桥梁,这个桥梁可以获取到环境变量,然后这个桥梁再把值传给JS
    (1) 首先,shell 可以直接获取到环境变量
    (2) docker 在构建镜像时就可以运行 shell 脚本
    (3) docker 可以做这样一个桥梁,获取到环境变量然后再把值注入到 html 文件中
    这就是上面那篇文章实现的缘由。
    后来,渐渐的想到,既然docker构建镜像时可以运行shell,那为什么还要去注入变量,然后 JS 再获取变量呢?不可以直接进行文本替换吗?当我想到的时候,简直就是一句点醒梦中人。
2.2 实现
2.2.1 获取系统环境变量

env 查看 然后 $ 对应名字即可,比如 $PATH
下面是在 shell 脚本中获取环境变量

#!/bin/bash
path=$PATH
2.2.2 sed 文本替换

简单说明一下 sed 命令
全局替换

sed 's/text/replace_text/g' file

默认替换后,输出替换后的内容,如果需要直接替换源文件,使用 -i

sed -i 's/text/replace_text/g' file

(1) 首先,做一个简单的测试。
在这里插入图片描述
创建两个文件,a.txt 和 run.sh
a.txt

arrow is a frontend engineer.
I am arrow

run.sh

#!/bin/bash
sed -i 's/arrow/arrow_zb/g' ./a.txt

运行 run.sh 文件

sh run.sh

运行 run.sh 后,a.txt 文件变为

arrow_zb is a frontend engineer.
I am arrow_zb	

这里需要注意一下,如果是在 mac 里执行,直接运行 sed -i 's/arrow/arrow_zb/g' ./a.txt 会报如下错误:
sed: 1: "./a.txt": invalid command code .
这是因为 macOS 下强制要求备份你修改的文件,当前,你可以使用空字符来代替,因此,macOS 下修改为以下代码即可运行
sed -i ' ' 's/arrow/arrow_zb/g' ./a.txt

(2) 实际获取环境变量替换文件内容
通过以上测试,我想,你应该已经知道如何实现我们想要的功能了,不过我这里还是记录一下,因为这个过程中我还是遇到了一些问题,因为我使用的环境变量比较特殊,具体特殊之处往下一看便知
假设我们想要获取系统变量 PATH, 将 a.txt 中的 arrow 替换成 $PATH
根据上面的测试,我们可以写出如下脚本,run.sh 文件。

sed -i '' "s/arrow/$PATH/g" ./a.txt

值得一提的是,shell 脚本的变量只能在双引号里才能识别,因此这里需要改成双引号

此时运行 run.sh,理想状况下会直接将 a.txt 里面的 arrow 替换成 $PATH 的内容。
但是,运行就会报错

sed: 1: "s/arrow//usr/local/bin: ...": bad flag in substitute command: 'u'

原因很简单,你将 PATH 打印出来就知道了

$ echo $PATH
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin

PATH 里面含有 / , 与此同时,我们的 sed -i '' "s/arrow/$PATH/g" ./a.txt 里的正则表达式定界符就是 / ,所以自然命令就混乱了,解决办法就是,将正则表达式定界符改成特别的即可,因为sed您可以使用任何定界符,例如改成如下:

sed -i '' "s~arrow~$PATH~g" ./a.txt

此时,运行即可生效,a.txt 的内容变成

/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin is a frontend engineer.
I am /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin

即达到了我们想到的目的。
到此,实现了我们的功能,而且只用了一行代码,极度极度的优雅!

2.3 实际应用

在实际的应用中,我们一般是想通过获取系统的环境变量来实现动态的在某个文件中修改值或实现功能,在这里我就以获取系统的 ENV_SYSTEM (取值共 dev, text, beta, prod),来改变 api.js 文件中的 ENV_NOW.

假设当前文件结构如下

.
├── run.sh
└── src
    └── api.js

api.js 中内容如下:

const origin = "www.ENV_NOW.arrow-zb.cn";

export default {
  prefix: origin,
  get_user_list: {
    get: '/api/user/list'
  }
}

想要通过系统环境变量实现

// dev
"www.dev.arrow-zb.cn"
// test
"www.test.arrow-zb.cn"
// beta
"www.beta.arrow-zb.cn"
// prod
"www.arrow-zb.cn"

那么在 run.sh 中首先应该获取系统变量,然后判断,然后替换即可。
以下就是简单的 shell 脚本了。

#!/bin/bash
ENV_NOW="."
if [ $ENV_SYSTEM != '' ]
then
  if [ $ENV_SYSTEM == 'prod' ]
  then
    ENV_NOW="."
  else
    ENV_NOW=".${ENV_SYSTEM}."
  fi
fi

sed -i '' "s/.ENV_NOW./$ENV_NOW/g" ./src/api.js

测试:

  • 系统变量 ENV_SYSTEMdev
    运行 run.sh
    src/app.js 结果为
const origin = "www.dev.arrow-zb.cn";

export default {
  prefix: origin,
  get_user_list: {
    get: '/api/user/list'
  }
}
  • 系统变量 ENV_SYSTEMprod
    运行 run.sh
    src/app.js 结果为
const origin = "www.arrow-zb.cn";

export default {
  prefix: origin,
  get_user_list: {
    get: '/api/user/list'
  }
}

完美的实现了预期的结果。
相信大部分同学看到这里已经知道如何在自己的项目中使用了,你就没必要看第三部分了。如果你还不知道,看第三部分,以真实的项目为例。

3 真实项目 —— 以 vue 为例

这里博主就不赘述简单的项目创建等,下面是使用 vue-cli 创建的简单示例。接下来一步一步的带领大家在真实项目中使用。
在这里插入图片描述

假设我在 src/api.js 中要 2.3 的需求。
同时我在 app.vue中将api打印出来做测试
在这里插入图片描述

3.1 在根目录创建 run.sh 文件
#!/bin/bash
ENV_NOW="."
if [ $ENV_SYSTEM != '' ]
then
  if [ $ENV_SYSTEM == 'prod' ]
  then
    ENV_NOW="."
  else
    ENV_NOW=".${ENV_SYSTEM}."
  fi
fi

# mac 下
# sed -i '' "s/.ENV_NOW./$ENV_NOW/g" ./src/api.js
# 其他linux下
sed -i "s/.ENV_NOW./$ENV_NOW/g" ./src/api.js

npm i
npm run build
3.2 在根目录创建Dockerfile文件
# 以nginx为基础镜像
FROM nginx
# 复制 ./dist/ 到 /usr/share/nginx/html/
COPY ./dist/ /usr/share/nginx/html/
# /usr/share/nginx/html/为 nginx 默认的启动文件夹
3.3 在宿主机中运行

将代码clone或移动到宿主机,运行 run.sh,其实一旦运行完成,就实现了获取宿主机的环境变量。

bash run.sh
3.4 docker构建并运行
docker build -t programming-career .
# 根据dockerfile来创建programming-career镜像
docker run -p 6090:80 -d -t programming-career
3.5 测试

运行直接测试即可,博主将宿主机的ENV_SYSTEM设置为了beta, 最终博主的测试结果如下,完美的实现了
在这里插入图片描述
在这里贴出测试代码 点击查看

4 总结

从有这个想法到测试成功再到今天写博客,前后花了大量的时间,特别是写博客,更是花了不小于10个小时的时间,现在晚上快 9 点了,最终完成,虽然可能文本逻辑上还是不太好,但是尽量的做到了简单易懂,而且做了对应的测试,还适应了 mac 和 linux ,算是比较完整了。

其实,只要理解了思路,你就会发现,其实很简单,而且很容易想到。

### 回答1: 这种问题通常是由于前后端字符集不一致导致的,可以尝试以下解决方案: 1. 确认前后端字符集统一为 UTF-8,可以在后台代码中设置响应头的字符集编码为 UTF-8: ``` response.setCharacterEncoding("UTF-8"); ``` 2. 在前端代码中指定字符集编码为 UTF-8: ``` <meta charset="utf-8"> ``` 3. 确认返回的数据格式为 json,可以在后台代码中将数据转换为 json 格式: ``` import com.alibaba.fastjson.JSON; String jsonString = JSON.toJSONString(data); response.getWriter().write(jsonString); ``` 4. 在前端代码中使用 JSON.parse() 方法将返回的数据解析为 json 格式: ``` var jsonData = JSON.parse(responseData); ``` 如果以上方法都无法解决问题,可以考虑使用第三方库如 iconv-lite 对字符集进行转换。 ### 回答2: 当JAVA后台接口返回的中文,在js前端获取到的时候出现乱码的情况,一般是由于字符编码的问题所致。 首先,我们需要确保JAVA后台接口返回的中文数据是以UTF-8编码方式进行返回的。可以通过设置Content-Type响应头的编码方式为UTF-8来实现: ``` response.setContentType("text/html;charset=UTF-8"); ``` 或者可以在返回数据之前,将中文数据进行UTF-8编码: ``` String result = new String(data.getBytes("UTF-8"), "ISO-8859-1"); ``` 其次,在前端使用JavaScript获取到中文数据时,需要确保使用合适的字符编码方式进行解码。一种常用的解决方法是使用decodeURI或decodeURIComponent函数进行解码: ``` var decodedData = decodeURIComponent(responseData); ``` 此外,还需要确保前端页面的字符编码声明正确,一般需要在head标签中添加如下代码指定页面使用UTF-8编码: ``` <meta charset="UTF-8"> ``` 如果仍然无法解决中文乱码问题,可能还需要检查其他方面是否存在字符编码的冲突,比如数据库连接、使用的框架等。在处理中文乱码问题时,需要保证各个环节都采用一致的字符编码方式,才能正确地显示中文数据。 ### 回答3: 当Java后台接口返回的是中文数据时,如果在前端通过JavaScript获取到的数据显示为"??????",可能是由于字符编码不匹配导致的。 在这种情况下,首先需要确认后台接口返回数据的字符编码是正确的,一般情况下应该使用UTF-8编码格式进行返回。可以在后台代码中进行设置或在接口返回时进行指定。 其次,确保前端页面中的字符编码也是正确的。可以在HTML的`<head>`标签中添加`<meta charset="UTF-8">`来指定页面使用UTF-8编码。 如果以上两个步骤都正确,但仍然出现"??????"的情况,那么可能是在数据传输过程中出现了乱码。在传输过程中,可以使用URL编码和解码来确保数据的正确传输。后台接口在返回数据时,可以对中文数据进行URL编码;前端获取数据时,需要对接收到的数据进行URL解码。 另外,如果是使用Ajax请求获取数据,可以在使用`XMLHttpRequest`对象发送请求之前,设置`xmlhttprequest.responseType = "text"`,确保返回的数据是文本类型。 综上所述,如果Java后台接口返回的是中文,而前端通过JavaScript获取的数据是"??????",可以先确认字符编码是否正确、使用URL编码和解码,以及设置响应类型为文本等措施来解决乱码问题。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangpaopao0609

看星空看日落不如看我的眼眸

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值