最近在工作中,听闻同事抱怨在内部平台上构建项目很慢。初步分析了一下,原因无非两个:1. 项目本身较大,导致构建时间较长 2. 我们的构建部署流程是:一次build qa和prod环境2个包,然后再分别部署不同环境的构建产物,无疑这样会导致整体构建时间变长。
前者不是本文的重点,本文只讨论第二点:即如何 一次构建多环境部署。
问题
build once deploy anywhere 在后端已经是比较常见了,但是对于前端来说,会有几点问题
1.环境变量
即 process.env.XXX 会在build阶段直接被编译成当前值
举例: process.env.RUNTIME 在被编译之后,就已经在代码中固定为 ‘this is qa runtime’,这意味着我们不能及时更改它。如果想要针对不同环境,更改为不同的值,那就需要重新构建
export default function IndexPage() {
return (
<div>
<p> `{process.env.RUNTIME}` </p>
</div>
);
}
// 在经过yarn build之后这段代码就成了
function o() {
return Object(r["jsx"])("div", {
children: Object(r["jsxs"])("p", {
children: [" `", "this is qa runtime", "` "]
})
})
}
要解决这个问题,最简单的办法就是将所有配置写到一个config.json中,使用fetch将其在代码中引入。
public/config.json
{
"dev":{
"RUNTIME": "this is dev runtime"
},
"qa":{
"RUNTIME": "this is qa runtime"
},
"prod":{
"RUNTIME": "this is prod runtime"
}
}
在 umi app.tsx中引入(项目部署时,会将部署信息 delopyEnv注入进来,用于项目判断是处于dev,qa,prod中的哪个环境)
// app.tsx
export function render(oldRender: any) {
fetch('./config.json')
.then((res) => res.json())
.then((config) => {
window.config = config[window?.APP_METADATA?.deployEnv|| 'dev'];
oldRender()
})
}
// pages/home/index.tsx
export default function IndexPage() {
return (
<div>
<h1 className={styles.title}>Page index</h1>
<p> `{window.config.RUNTIME}` </p>
</div>
);
}
至此 我们可以看到,我们就可以使用window.config.RUNTIME 来代替 process.env.RUNTIME
2.publicPath
我们在本地运行的时候,publicpath一般就为根目录地址。但是在线上环境的话,一般 会将css js img等资源文件托管到CDN上。
即我们在qa环境构建出来的资源文件 会以类似<script src="https://static.qa.fiture.com/h1t86b7fg6c7k17v78ofck5d/umi.f6afc434.js"></script>的形式存在于项目中。
若要动态的去根据环境改变访问img script link 地址的话,
目前我的思路是:在打包的时候使用占位符作为publicpath。等到启动项目的时候,运行一个脚本,将占位符替换掉即可。
我们设置非本地环境时的publicpath 为$$root/作为占位符
const publicPath = development ? './' : '$$root/';
那么对应的构建产物index.html就会是
<head>
<link rel="stylesheet" href="$$root/umi.css" />
<script>
//! umi version: 3.5.20
</script>
</head>
<body class="body">
<div id="root"></div>
<script src="$$root/umi.js"></script>
</body>
故我们可以在部署前执行命令sed -i "" "s/\$\$root/publicpath/g" dist/*
将$$root 替换成真正的public path路径(部署时由环境变量注入).
最后我们将其写成一个脚本 在scripts/replacePath.js中 并添加到package.json中
//package.json
"scripts": {
"replacePath": "node scripts/replacePath"
},
// replacePath.js
const child_process = require('child_process')
const { cwd } = require('process')
child_process.spawn('bash', ['./scripts/bash.sh'], {
cwd: cwd(),
shell: true,
});
// bash.sh
#!/bin/bash -x
sed -i "" "s/\$\$root/publicpath/g" dist/*
最后附上对应的代码 demo地址
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。