前端集成:如何把n个前端写的代码打包成一个网站?

前端集成:如何把n个前端写的代码打包成一个网站?

原答案:欲三更:大公司里怎样开发和部署前端代码?


非覆盖发布,CDN,CI工作流,大家都说的挺全的了。所以我站在自己的角度,补充一点“前端项目如何把代码集成在一起”的方法。


这里说的集成不是webpack打包,有点规模的项目,不适合采用单一工程打包,哪怕有dynamic import也是一样。一个完整的互联网产品,上百屏甚至几百屏,需要支持灰度发布、局部提测等必要流程,还要应付线上的各种灵活配置需求,仅仅一个webpack工程,是应付不来的。


现在后端流行微服务,基本上所有的发布都是局部发布,没有说我这个网站今天晚上发布,就把所有的服务重新部署一遍的。那前端呢?前端肯定也不能这么干。


先说明一下,前后端分离中的一个要点就是发布分离,如果你的前端发布流程还依赖后端发布,那就没法聊了。咱们讨论的基础,就是前端有CDN或者独立的静态服务器环境,独立的CI,以及前端版本配置中心。


考虑一种最简单的场景:


第一,可停机。怎么叫可停机呢?就是你这个网站,在一天的某一时刻,是可以下线的,或者说在一天的某一时刻,PV近似为0。因为如果不是这样,比如说知乎吧,知乎24小时都有大量用户使用,所以知乎的发布就得考虑前后端兼容问题(要么就得通知用户“xx时段知乎线上更新,出现故障请谅解”),你新服务上线了,已经打开的老页面怎么办?新页面被打开了,老服务不支持怎么办?所以那些海量用户的大型互联网产品发布重要版本的时候,都要依赖一定的流量调度手段(比如最典型的就是QQ),这个还是比较复杂的,因为我们讨论的是前端集成,所以暂不涉及这方面内容。


第二,不考虑SSR的情况,假设我们的网站是纯前端渲染的,也不考虑BFF服务的发布,只考虑页面发布。因为考虑SSR和BFF的话,前端发布就和后端发布没什么两样了。


对于这种简单场景,我目前采用的集成和部署方式大致可以由下面这个框图描述:


其中FE gateway就是一个中间服务层,担负了网站的入口和各种配置管理功能,其中和前端集成有关系的,主要就是模板托管,页面管理,以及变量管理。从图中我们可以看出这三个模块的工作方式,就是页面管理负责提供可用的URL,模板托管负责管理后端模板,变量管理负责管理后端模板里面的各种变量的值,当然也包括前端资源的版本号。这三个模块中的数据merge在一起,就是一个完整的html页面。


在这种模式下,前端发布版本的工作流程就是首先把打好包的前端资源推到CDN上,因为是非覆盖发布,所以静态资源的URL中是含有版本号的。然后在变量管理中将你要发布的模块的版本号改为最新的,发布就完成了。


我提前配置好的模板中,有类似这样的内容:

<script src='//my.cdn.com/xxx/${xxx-version}/bundle.min.js'></script>

我把“xxx-version”这个变量的值改为1.0.1,用户下次访问的时候,自然就会或许1.0.1版本的前端资源。


这样的结构在实际项目中会有各种变体,比如最常见的,很多项目中会存在一个nginx逆向代理,把接口访问转发到后端的API入口,把页面访问转发到专门的前端服务器,此时FE gateway就是那个前端服务器,如果你不愿意为此做一个网站,或者嫌从数据库里面取这些数据太慢,你完全可以把它们都放在硬盘甚至内存里。


这种结构有一个好处,就是我可以把我的前端页面切分成多个单独的前端工程,愿意的话一个页面一个工程都可以(虽然想不出来这有什么意义),我可以根据需要灵活的组合这些项目的版本号。对于那种一个产品到处部署的项目,或者那种“产品是个筐,什么都往里装”,连PD都搞不明白里面都有些啥的项目,这种机制再合适不过了。坏处也显而易见——版本太碎了。


有了发布机制,咱们就得考虑一下前端怎么打包。这里说的打包,不是一个项目里面怎么打包,而是项目和项目之间,怎么打包集成。上面说过了我们的页面被横向切成了很多项目,但是这些项目中肯定有很多复用的部分,如果重复打包,或者都往CDN上推,肯定是不合适的。


目前我采用的方式大致如下图:


首先,页面工程和第三方库(比如react)肯定是往CDN上推的,而组件库和基础库,更适合采用npm package的发布方式。所谓公共库,是把全局使用或者高频使用的组件和基础库代码打包成为bundle单独在CDN上发布的(不一定是一个bundle,更合理的是组件一个,非组件一个)。在构建页面工程的时候,一些局部使用的组件或者库函数,会直接通过npm install的方式引入工程,进行常规的webpack打包(嫌大就code split),而全局使用的组件和基础库,通过配置webpack external将其引用指向全局变量,从而引用公共库中的内容。


这里有一个问题,就是如果你有一个通用组件库,比如Ant Design这样的,是不是一定要打包到公共库中呢?其实我不建议这样做,因为组件库的size可能会非常大,这种情况下把全量组件都打包推到CDN上引用,是非常不划算的。


那为什么还要有公共库呢?直接用webpack打包+code split不行吗?主要理由有两个:


第一个理由是,因为我们的前端被切分成了多个项目,所以项目之间的复用,用code split搞不定。


第二个理由是便于管理,因为公共库的版本号,是可以由一个人掌控的,当这个人决定要升级公共库版本的时候,所有的页面工程开发人员必须进行适配。这样就避免了到处催开发人员升级基础代码版本的问题。


以上就是我想介绍的前端集成方面的一些方法,并没有什么深奥的知识,也远远谈不上最优,但是工程上的东西往往是有比没有好,有一个标准的方案摆在那里,不好可以改进,但是如果是无方案的混乱开发,那想改进都不知道该改进什么。

编辑于 2019-04-08 18:38