前端开发那些事让前端飞

基于gitlab CI 搭建前端页面预览服务

2019-03-01  本文已影响364人  RichardBillion

前端在粗放开发模式下的痛点

前端业务在近几年迎来一个很好的发展,但关于前端的基础设施并没有跟上前端业务的迅速扩展。业务扩张之后,我们不能再像小作坊一样进行粗放的开发:开发前如何快速规范的初始化项目?开发中如何保证多人高效的合作开发?开发完成后如何保证正确快速的上线?上线后如何管理诸多业务稳定的运行?围绕这些问题,笔者列举一些相关的基础设施:完整的构建打包流程/服务(统一的脚手架、上线服务等)、完整的测试环境、前端错误日志管理系统(收集、统计、报警)、前端资源离线化管理、前端资源增量下载服务以及针对Node应用的日志(完整调用链)、性能和错误监控平台等等。

其中,针对前端业务在上线前,我们一直有这样的一个痛点:基于现有项目在继续开发时,本地开发完成后,需要启动本地服务,预览给PM查看检查,但有时PM或者团队其他人员想看下效果,而自己又不方便操作电脑,就总是需要协调时间。如果自己开发完成后,可以直接上线到一个测试环境,将链接丢到群里,会非常方便别人随时预览效果。

所以,本文的目标是: 针对前后端分离的前端项目,git push 之后,能够直接推到测试环境,可以在线预览效果。

gitlab CI 简介

从v 7.1.2 之后,gitlab支持通过配置.gitlab-ci.yaml文件支持CI/CD。
具体配置可参考文档: https://docs.gitlab.com.cn/ee/ci/yaml/

为了使项目能够执行yaml文件中配置的task,还需要我们首先部署安装相应的环境: install runner && register runner, 参考:https://docs.gitlab.com/runner/#using-gitlab-runner。runner的执行方式有很多种, 目前最流行的就是作为一个docker容器,其内部集成了gitlab的一些基础环境, 注册阶段就是将其与gitlab主任务做关联(runner通常不跟gitlab服务器部署在同一台服务器),而yaml中配置的任务,就是在runner中具体执行, 然后将结果发送回gitlab服务器。

最后项目需要在setttings中开启enable shared runner或者specific runner.

基于Node搭建前端业务的预览服务

使用Node搭建服务,托管静态资源,以及代理请求的转发。

基本流程

基本流程比较好理解,但囿于公司现有基础设施的限制,一些问题变得复杂一些:

静态资源的上传

上面说到,需要首先获取部署了node服务的所有实例地址,然后进行上传, 如何上传呢?

使用后者作为解决方案:

PS: koa 的async, await与操作文件时的stream配合总觉得有点tricky: 需要将stream的操作形式转为promise, 如:

function pipe(from, to, options) {
    return new Promise((resolve, reject) => {
        from.pipe(to, options)
        from.on('error', reject)
        from.on('end', resolve)
    })
}

async function processZipFiles(input, output) {
    const reader = fs.createReadStream(input);
    const upStream = fs.createWriteStream(output);
    await pipe(input, output);
}

接口代理的处理

每个项目都需要指定其真实后端的请求域名,这样才能够对项目中的请求进行转发。初次之外,还需要支持将某些接口代理到其他指定地址,如webpack dev server所支持的那样。

所以我们支持两种方式,

使用这种方式,还可以继续支持以后添加除了proxyApi的配置,为以后业务的扩展提供了余量。

如何在接口请求中注入项目的相关信息?

因为所有项目公用一个域名,紧靠路径来区分不同项目,但是接口请求时却都是域名+接口进行拼接,所以我们需要针对不同项目,在其接口中添加关于项目信息的前缀:针对测试环境,在打包时,将其请求接口地址由/api/xxx改为/project1/branch1/api/xxx。但是实际修改文件中的每个地址是不现实的,我们无法准确识别哪些地方是需要添加前缀的。而前端进行网络请求的方式就两种XMLHttpRequest和fetch, 所有我们只要在html文件最前面对其方法进行改写即可。

function buildUrl(prefix) {}

var originXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  return originXHROpen.call(this, method, buildUrl(url, '${prefix}'), async, user, password);
};

if (window.fetch) {
  var originFetch = window.fetch;
  window.fetch = function () {
    var input = arguments[0];
    
    if (typeof input === 'string') {
      arguments[0] = buildUrl(input, '${prefix}');
    }
    
    return originFetch.apply(this, arguments);
  };
}
该配置如何发送到node服务?

后者显然为更优方案。

静态资源、proxy config实例化

上文提到静态资源是直接发送到每台实例,proxy config也是发送到每个node实例,然后直接修改内存中的config。倘若node服务重启,docker容器新建,这些东西不就全部丢失了吗?所以需要对其进行静态化存储,当node重启服务时,从此读取初始值。

多路由业务的支持

因为团队现在统一使用react技术栈,所以对于多路由的支持就围绕react-router-dom进行。通常会使用的路由组件是BrowserRouter或者StaticRouter, 而其
basename参数可以用来对url地址添加前缀,这跟上文中我们需要的项目相关信息完全符合,所以我们可以通过修改其basename参数实现对多路由的支持。

选用方案2。 基于webpack4 提供的parser api 来解析被webpack处理过的每个module, 类似 useStrictPlugin.js实现, 只是在得到ast后再利用babel的‘traverse‘和'generate'包生成修改了basename的方法。

【参考】

gitlab CI官方介绍
当谈到 GitLab CI 的时候,我们该聊些什么(上篇)

上一篇 下一篇

猜你喜欢

热点阅读