之前一个前后端分离的项目中,一个 Web Server 同时承载前端资源和 Web API。因此前端可在同域下访问 Web API。但在开发阶段,这个逻辑可能并不成立。
比如大前端团队(上图的 Front-End)中,有小 a、小 b、小 c 三个成员。某一期任务中,小 a 主要负责前端页面,而小 b 和小 c 负责提供相应的 Web API。
小 a 访问在自己电脑上开发好的前端页面,使用的 URL 是 http://localhost:3000/xxx
。当他需要与小 b 联调时,就需要在 JS 中使用类似 http://小b:3001/api/xxx
这样的 URL。那么对于浏览器来说,这两组URL 便不是同域的。这就意味着,前端页面不能直接使用相对地址(如 /api/xxx
)来访问 API。
除了与小 b 联调,小 a 可能还要,与小 c 联调,与本机的 mock API联调,与开发服务器甚至测试服务器联调。
由此就产生了一个需求,“在开发阶段,前端需要 API 域
是可配置的”。但要明确一点,后续在测试环境、生产环境部署的前端 Web Server,因为此例中没有其余导致需要跨域访问的限制,在页面中是可以使用相对地址访问 API 的。
以产生时机划分,配置有两种方式,一种在程序构建时注入,一种在程序运行时产生。
《高性能 Javascript》中有一节提及,“开发高性能应用的一个普遍规则是,只要是能在构建时完成的工作,就不要留到运行时去做”。而此例中,虽然没有性能方面的考虑,但依然优先考虑构建时处理。
理由是,如果在运行时处理,意味着承载前端资源的 Web Server,需要引入额外的服务端代码,才能将运行时产生的配置,喂给前端页面,会增加了不少的复杂性。
构建时注入配置,需要三个步骤:
- 在前端代码中,定义一个需要构建时预处理的标记
const API_DOMAIN = '${API_DOMAIN}' || '' // 默认为空,即以相对地址形式访问 API
// 请求 xxx API
request.get(`${API_DOMAIN}/api/xxx`, () => {
})
- 在实现构建过程的代码中,利用构建工具替换上述标记,以 Gulp 为例,
gulp.src(['main.js'])
.pipe(replace('${API_DOMAIN}', process.env.API_DOMAIN)) // 使用环境变量
.pipe(gulp.dest('dist/'));
- 小 a 可在本机运行下述命令执行前端构建
# 与小 b 联调
API_DOMAIN=http://小b:3001 npm run build
# 或与小 c 联调
API_DOMAIN=http://小c:3002 npm run build