在 umi 中实现一次构建多环境部署
最近在工作中,听闻共事埋怨在外部平台上构建我的项目很慢。初步剖析了一下,起因无非两个: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 地址