Blog

Blog

PHODAL

前端微服务化方案对比:路由懒加载 vs 子应用模式

撸了个新框架 Mooa,用于前端应用的微服务化拆分。

过去的几周里,作为一个 “专业” 的咨询师,一直忙于在为客户设计一个应用拆分的服务化方案。主要是为了达成以下的设计目标:

  • 构建插件化的 Web 开发平台,满足业务快速变化及分布式多团队并行开发的需求
  • 构建服务化的中间件,搭建高可用及高复用的前端微服务平台
  • 支持前端的独立交付及部署

简单地来说,就是要支持应用插件化开发,以及多团队并行开发

应用插件化开发

应用插件化开发,其所要解决的主要问题是:臃肿的大型应用的拆分问题。大型前端应用,在开发的时候要面临大量的遗留代码、不同业务的代码耦合在一起,在线上的时候还要面临加载速度慢,运行效率低的问题。

前端微服务化:路由懒加载

路由懒加载,即通过不同的路由来将应用切成不同的代码快,当路由被访问的时候,才加载对应组件。

在诸如 Angular、Vue 框架里都可以通过路由 + Webpack 打包的方式来实现。不可避免地就会需要一些问题:

难以多团队并行开发,路由拆分就意味着我们仍然是在一个源码库里工作的。也可以尝试拆分成不同的项目,再编译到一起,可能会导致以下问题:

运行效率变慢,在这种情况下使用懒加载时,无法使用 AoT(Ahead-of-time,提前编译),只能使用 JIT(Just In Time即时编译)。使用 AOT,浏览器下载预编译版本的应用程序,而不是等应用完成首次编译——不过,编译 HTML 和 CSS 编译后的 JavaScript 体积更大。它也就意味着,我们需要引入整个 Angular 编译器,该编译器差不多占了 Angular 自身体积的一半儿。

首屏加载可能没有变快,当我们使用 Webpack 来分开打包的时候,目前的 Webpack 没有办法区分中那些方法是用不到的函数,与此同时还不能对 js 进行 uglify,因为子模块可能找不到对应的依赖。这就意味着,有大量的不需要用到的 JS 代码,被加入到了 Vendor 文件中;与此同时,它们还是没有压缩(uglify)的代码。

每次发布需要重新编译,是的,当我们只是更新一个子模块的代码,我们要重新编译整个应用,再重新发布这个应用。而不能独立地去构建它,再发布它。

统一的 Vendor 版本,统一第三方依赖是一件好事。可问题的关键在于:每当我们添加一个新的依赖,我们可能就需要开会讨论一下。

然而,最大的问题就是难以多团队并行开发,这里之所以说的是 “难以” 是因为,还是有办法解决这个问题。在日常的开发中,一个小的团队会一直在一个代码库里开发,而一个大的团队则应该是在不同的代码库里开发。

对于一个二三十人规模的团队来说,他们可能在业务上归属于不同的部门,技术上也有一些不一致的规范,如 4 个空格、2 个空格还是使用 Tab 的问题。特别是当它是不同的公司和团队时,他们可能要放弃测试、代码静态检测、代码风格统一等等的一系列问题。

微服务化方案:子应用模式

除了路由懒加载,我们还可以采用子应用模式,即每个应用都是相互独立地。即我们有一个基座工程,当用户点击相应的路由时,我们去加载这个独立 的 Angular 应用;如果是同一个应用下的路由,就不需要重复加载了。而且,这些都可以依赖于浏览器缓存来做。

除了路由懒加载,还可以采用的是类似于 Mooa 的应用嵌入方案。如下是基于 Mooa 框架 + Angular 开发而生成的 HTML 示例:

<app-root _nghost-c0="" ng-version="4.2.0">
  ...
  <app-home _nghost-c2="">
    <app-app1 _nghost-c0="" ng-version="5.2.8" style="display: none;"><nav _ngcontent-c0="" class="navbar"></app-app1>
    <iframe frameborder="" width="100%" height="100%" src="http://localhost:4200/app/help/homeassets/iframe.html" id="help_206547"></iframe>
  </app-home>
</app-root>

Mooa 提供了两种模式,一种是基于 Single-SPA 的实验做的,在同一页面加载、渲染两个 Angular 应用;一种是基于 iFrame 来提供独立的应用容器。

解决了以下的问题:

  • 首页加载速度更快,因为只需要加载首页所需要的功能,而不是所有的依赖。
  • 多个团队并行开发,每个团队里可以独立地在自己的项目里开发。
  • 独立地进行模块化更新,现在我们只需要去单独更新我们的应用,而不需要更新整个完整的应用。

但是,它仍然包含有以下的问题:

  • 重复加载依赖项,即我们在 A 应用中使用到的模块,在 B 应用中也会重新使用到。有一部分可以通过浏览器的缓存来自动解决。
  • 第一次打开对应的应用需要时间,当然预加载可以解决一部分问题。
  • 在非 iframe 模式下运行,会遇到难以预料的第三方依赖冲突。

其它对比如下:

. 标准 Lazyload 模块独立 应用独立
依赖管理 统一管理 统一管理 各应用独立管理
部署方式 统一部署 可单独部署。更新依赖时,需要全量部署 可完全独立部署
首屏加载 依赖在同一个文件,加载速度慢 依赖在同一个文件,加载速度慢 依赖各自管理,首页加载快
首次加载应用、模块 只加载模块,速度快 只加载模块,速度快 单独加载,加载略慢
前期构建成本 设计构建流程 设计通讯机制与加载方式
维护成本 一个代码库不好管理 后期需要维护组件依赖 后期维护成本低
打包优化 可进行摇树优化、AoT 编译、删除无用代码 应用依赖的组件无法确定,不能删除无用代码 可进行摇树优化、AoT 编译、删除无用代码

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签