At work recently, I heard colleagues complain that building projects on internal platforms is slow. After a preliminary analysis, the reasons are nothing more than two: 1. The project itself is large, which leads to a long build time. 2. Our build and deployment process is: build 2 packages in the qa and prod environments at one time, and then deploy the build products in different environments respectively. , which will undoubtedly lead to a longer overall build time.
The former is not the focus of this article, and this article only discusses the second point: how to build a multi-environment deployment at once.
question
build once deploy anywhere is relatively common in the backend, but for the frontend, there will be several problems
1. Environment variables
That is, process.env.XXX will be directly compiled into the current value in the build phase. Example: After process.env.RUNTIME is compiled, it has been fixed as 'this is qa runtime' in the code, which means that we cannot change it in time . If you want to change to a different value for different environments, you need to rebuild
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", "` "]
})
})
}
To solve this problem, the easiest way is to write all the configuration to a config.json, use fetch to import it in the code.
public/config.json
{
"dev":{
"RUNTIME": "this is dev runtime"
},
"qa":{
"RUNTIME": "this is qa runtime"
},
"prod":{
"RUNTIME": "this is prod runtime"
}
}
Introduced in umi app.tsx (when the project is deployed, the deployment information delopyEnv will be injected into the project to determine which environment it is in 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>
);
}
So far we can see that we can use window.config.RUNTIME instead of process.env.RUNTIME
2. publicPath
When we run locally, the publicpath is generally the root directory address. However, in an online environment, resource files such as css js img are generally hosted on CDN.
That is, the resource file we build in the qa environment will exist in the project in the form of <script src="https://static.qa.fiture.com/h1t86b7fg6c7k17v78ofck5d/umi.f6afc434.js"></script>.
If you want to dynamically change the access img script link address according to the environment,
My current thinking is: use placeholders as publicpath when packaging. When the project starts, run a script and replace the placeholders.
The publicpath when we set the non-local environment is $$root/ as a placeholder
const publicPath = development ? './' : '$$root/';
Then the corresponding build product index.html will be
<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>
So we can execute the command before deployment
sed -i "" "s/\$\$root/publicpath/g" dist/*
Replace $$root with the real public path (injected by environment variables during deployment).
Finally we write it as a script in scripts/replacePath.js and add it to 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/*
Finally, attach the corresponding code demo address
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。