上一个月,我们花了大量的时间不熂设计方案来拆分一个大型的 Angular 应用。从使用 Angular 的 Lazyload 到前端微服务化,进行了一系列的讨论。最后,我们终于有了结果,采用的是 Lazyload 变体:构建时集成代码 的方式。

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

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

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

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

最后就落在了两个方案上:路由懒加载及其变体与前端微服务化

前端微服务化:路由懒加载及其变体

路由懒加载,即通过不同的路由来将应用切成不同的代码快,当路由被访问的时候,才加载对应组件。在诸如 Angular、Vue 框架里都可以通过路由 + Webpack 打包的方式来实现。而,不可避免地就会需要一些问题:

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

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

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

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

于是,我们在标准的路由懒加载之上做了一些尝试。

对于一个二三十人规模的团队来说,他们可能在业务上归属于不同的部门,技术上也有一些不一致的规范,如 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 模式下运行,会遇到难以预料的第三方依赖冲突。

于是在总结了一系列的讨论之后,我们形成了一系列的对比方案:

方案对比

在这个过程中,我们做了大量的方案设计与对比,便想写一篇文章对比一下之前的结果。先看一下图:

表格对比:

x 标准 Lazyload 构建时集成 构建后集成 应用独立
开发流程 多个团队在同一个代码库里开发 多个团队在同不同的代码库里开发 多个团队在同不同的代码库里开发 多个团队在同不同的代码库里开发
构建与发布 构建时只需要拿这一份代码去构建、部署 将不同代码库的代码整合到一起,再构建应用 将直接编译成各个项目模块,运行时通过懒加载合并 将直接编译成不同的几个应用,运行时通过主工程加载
适用场景 单一团队,依赖库少、业务单一 多团队,依赖库少、业务单一 多团队,依赖库少、业务单一 多团队,依赖库多、业务复杂
表现方式 开发、构建、运行一体 开发分离,构建时集成,运行一体 开发分离,构建分离,运行一体 开发、构建、运行分离

详细的介绍如下:

标准 LazyLoad

开发流程:多个团队在同一个代码库里开发,构建时只需要拿这一份代码去部署。

行为:开发、构建、运行一体

适用场景:单一团队,依赖库少、业务单一

LazyLoad 变体 1:构建时集成

开发流程:多个团队在同不同的代码库里开发,在构建时将不同代码库的代码整合到一起,再去构建这个应用。

适用场景:多团队,依赖库少、业务单一

变体-构建时集成:开发分离,构建时集成,运行一体

LazyLoad 变体 2:构建后集成

开发流程:多个团队在同不同的代码库里开发,在构建时将编译成不同的几份代码,运行时会通过懒加载合并到一起。

适用场景:多团队,依赖库少、业务单一

变体-构建后集成:开发分离,构建分离,运行一体

前端微服务化

开发流程:多个团队在同不同的代码库里开发,在构建时将编译成不同的几个应用,运行时通过主工程加载。

适用场景:多团队,依赖库多、业务复杂

前端微服务化:开发、构建、运行分离

总对比

总体的对比如下表所示:

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

前端微服务化:拆分大型 Angular 应用的四种策略的更多相关文章

  1. 前端监听websocket消息并实时弹出(实例代码)

    这篇文章主要介绍了前端监听websocket消息并实时弹出,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. 前端实现背景虚化但内容清晰且自适应 的实例代码

    这篇文章主要介绍了前端实现背景虚化但内容清晰且自适应 的实例代码,需要的朋友可以参考下

  3. 关闭iOS原生MPVolumeView音频路由菜单

    我正在使用MPVolumeView允许用户在使用我的应用程序时控制他喜欢的音频路径.该代码显示了该视图:当用户点击音频路由按钮时,会出现一个带有可用选项的菜单.问题:显示音量视图的屏幕可能需要隐藏,因为我的应用程序处理各种事件,我想同时隐藏音频路由菜单我的问题:有没有人知道是否可以手动关闭MPVolumeView的音频路由选择菜单而无需用户按下取消按钮?解决方法在iOS8上,您可以使用以下使用私有API的代码

  4. iOS:使用蓝牙音频输出(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput)AudioSession

    >如果有可用的A2DP设备,我的音频路由将始终自动切换到kAudioSessionOutputRoute_BluetoothA2DP路由.如何防止此路线更改?我希望你们中的一些人可以帮助我解决这些问题.这对我对CoreAudio的整体理解,特别是AudioSession框架,真的有帮助.解决方法AudioSession是一项棘手的业务.1.BluetoothHFPaudiooutputisonlypossibleincaseofAudioSessionkAudioSessionCategory_PlayA

  5. swift实现懒加载

    在swift中使用lazy描述符号可以实现属性的懒加载

  6. swift lazy 懒加载

    我们在使用lazy作为属性修饰符时,只能声明属性是变量。另外我们需要显式地指定属性类型,并使用一个可以对这个属性进行赋值的语句来在首次访问属性时运行。

  7. Swift中闭包,懒加载,单例的写法区别

    闭包闭包的参数和返回值都写在大括号里面,以”in”分隔开闭包内的代码块,如果闭包的参数和返回值都为空的话,”()->()in”就可以省略,下面是几种常见的闭包写法:最简单的闭包:有参数的闭包声明方法:方式1方式2,最外层括号可以前移到闭包名后面方式3懒加载:在Swift中,懒加载本质就是一个闭包,在前面加上lazy关键字,在需要这个属性的时候,会执行后面的闭包,并且把闭包的返回值记录下来,下次再次

  8. Swift- lazy 懒加载

  9. swift学习日志—— lazy懒加载

    在其他语言中懒加载的情况是很常见的。在Swift中我们使用在变量属性前加lazy关键字的方式来简单地指定延时加载。相比起在Objective-C中的实现方法,现在的lazy使用起来要方便得多。另外一个不太引起注意的是,在Swift的标准库中,我们还有一组lazy方法,它们的定义是这样的:这些方法可以配合像map或是filter这类接受闭包并进行运行的方法一起,让整个行为变成延时进行的。

  10. 从零学习Swift&lt;6&gt;

    构造函数convenience便利构造函数默认情况下,所有的构造方法都是指定构造函数Designatedconvenience关键字修饰的构造方法就是便利构造函数便利构造函数具有以下特点:可以返回nil只有便利构造函数中可以调用self.init()便利构造函数不能被重写或者super便利构造函数应用场景根据给定参数判断是否创建对象,而不像指定构造函数那样必须要实例化一个对象出来在实际开发中,可以

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部