如何解构单体前端应用——前端应用的微服务式拆分

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介:

刷新页面?路由拆分?No,动态加载组件。

本文分为以下四部分:

  • 前端微服务化思想介绍

  • 微前端的设计理念

  • 实战微前端架构设计

  • 基于 Mooa 进行前端微服务化

前端微服化

对于前端微服化来说,有这么一些方案:

  • Web Component 显然可以一个很优秀的基础架构。然而,我们并不可能去大量地复写已有的应用。

  • iFrame。你是说真的吗?

  • 另外一个微前端框架 Single-SPA,显然是一个更好的方式。然而,它并非 Production Ready。

  • 通过路由来切分应用,而这个跳转会影响用户体验。

  • 等等。

因此,当我们考虑前端微服务化的时候,我们希望:

  • 独立部署

  • 独立开发

  • 技术无关

  • 不影响用户体验

独立开发

在过去的几星期里,我花费了大量的时间在学习 Single-SPA 的代码。但是,我发现它在开发和部署上真的太麻烦了,完全达不到独立部署地标准。按 Single-SPA 的设计,我需要在入口文件中声名我的应用,然后才能去构建:

 
  1. declareChildApplication('inferno', () => import('src/inferno/inferno.app.js'), pathPrefix('/inferno'));

同时,在我的应用里,我还需要去指定我的生命周期。这就意味着,当我开发了一个新的应用时,必须更新两份代码:主工程和应用。这时我们还极可能在同一个源码里工作。

当出现多个团队的时候,在同一份源码里工作,显然变得相当的不可靠——比如说,对方团队使用的是 Tab,而我们使用的是 2 个空格,隔壁的老王用的是 4 个空格。

独立部署

一个单体的前端应用最大的问题是,构建出来的 js、css 文件相当的巨大。而微前端则意味着,这个文件被独立地拆分成多个文件,它们便可以独立去部署应用。

我们真的需要技术无关吗?

等等,我们是否真的需要技术无关?如果我们不需要技术无关的话,微前端问题就很容易解决了。

事实上,对于大部分的公司和团队来说,技术无关只是一个无关痛痒的话术。当一家公司的几个创始人使用了 Java,那么极有可能在未来的选型上继续使用 Java。除非,一些额外的服务来使用 Python 来实现人工智能。因此,在大部分的情况下,仍然是技术栈唯一。

对于前端项目来说,更是如此:一个部门里基本上只会选用一个框架。

于是,我们选择了 Angular。

不影响用户体验

使用路由跳转来进行前端微服务化,是一种很简单、高效的切分方式。然而,路由跳转地过程中,会有一个白屏的过程。在这个过程中,跳转前的应用和将要跳转的应用,都失去了对页面的控制权。如果这个应用出了问题,那么用户就会一脸懵逼。

理想的情况下,它应该可以被控制。

微前端的设计理念

设计理念一:中心化路由

互联网本质是去中心化的吗?不,DNS 决定了它不是。TAB,决定了它不是。

微服务从本质上来说,它应该是去中心化的。但是,它又不能是完全的去中心化。对于一个微服务来说,它需要一个服务注册中心

服务提供方要注册通告服务地址,服务的调用方要能发现目标服务。

对于一个前端应用来说,这个东西就是路由。

12f664e081c1274eb689e9d81a4c5da2bcfc5b89

Menu

从页面上来说,只有我们在网页上添加一个菜单链接,用户才能知道某个页面是可以使用的。

12b3f160526119262eddfdd4538278d155fd02cb

404

而从代码上来说,那就是我们需要有一个地方来管理我们的应用:**发现存在哪些应用,哪个应用使用哪个路由。

管理好我们的路由,实际上就是管理好我们的应用

设计理念二:标识化应用

在设计一个微前端框架的时候,为每个项目取一个名字的问题纠结了我很久——怎么去规范化这个东西。直到,我再一次想到了康威定律:

系统设计(产品结构等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构。

3653a1cef55cb90186a738c7bcc57f8b230aeabf

康威定律

换句人话说,就是同一个组织下,不可能有两个项目的名称是一样的。

所以,这个问题很简单就解决了。

设计理念三:生命周期

Single-SPA 设计了一个基本的生命周期(虽然它没有统一管理),它包含了五种状态:

  • load,决定加载哪个应用,并绑定生命周期

  • bootstrap,获取静态资源

  • mount,安装应用,如创建 DOM 节点

  • unload,删除应用的生命周期

  • unmount,卸载应用,如删除 DOM 节点

于是,我在设计上基本上沿用了这个生命周期。显然,诸如 load 之类对于我的设计是多余的。

设计理念四:独立部署与配置自动化

从某种意义上来说,整个每系统是围绕着应用配置进行的。如果应用的配置能自动化,那么整个系统就自动化。

当我们只开发一个新的组件,那么我们只需要更新我们的组件,并更新配置即可。而这个配置本身也应该是能自动生成的。

实战微前端架构设计

基于以上的前提,系统的工作流程如下所示:

7aa739e8c73af691871ad822956769b01468a0d8

系统工作流

整体的工程流程如下所示:

  1. 主工程在运行的时候,会去服务器获取最新的应用配置。

  2. 主工程在获取到配置后,将一一创建应用,并为应用绑定生命周期。

  3. 当主工程监测到路由变化的时候,将寻找是否有对应的路由匹配到应用。

  4. 当匹配对对应应用时,则加载相应的应用。

故而,其对应的架构如下图所示:

eb899a2e67a02cbdaed029b003408a74183d17c9

Architecture

独立部署与配置自动化

我们做的部署策略如下:我们的应用使用的配置文件叫 apps.json,由主工程去获取这个配置。每次部署的时候,我们只需要将 apps.json 指向最新的配置文件即可。配置的文件类如下所示:

  1. 96a7907e5488b6bb.json

  2. 6ff3bfaaa2cd39ea.json

  3. dcd074685c97ab9b.json

一个应用的配置如下所示:

 
  1. {

  2.  "name": "help",

  3.  "selector": "help-root",

  4.  "baseScriptUrl": "/assets/help",

  5.  "styles": [

  6.  "styles.bundle.css"

  7.  ],

  8.  "prefix": "help",

  9.  "scripts": [

  10.  "inline.bundle.js",

  11.  "polyfills.bundle.js",

  12.  "main.bundle.js"

  13.  ]

  14. }

这里的 selector 对应于应用所需要的 DOM 节点,prefix 则是用于 URL 路由上。这些都是自动从 index.html 文件和 package.json 中获取生成的。

应用间路由——事件

由于现在的应用变成了两部分:主工程和应用部分。就会出现一个问题:只有一个工程能捕获路由变化。当由主工程去改变应用的二级路由时,就无法有效地传达到子应用。在这时,只能通过事件的方式去通知子应用,子应用也需要监测是否是当前应用的路由。

 
  1. if (event.detail.app.name === appName) {

  2.  let urlPrefix = 'app'

  3.  if (urlPrefix) {

  4.  urlPrefix = `/${window.mooa.option.urlPrefix}/`

  5.  }

  6.  router.navigate([event.detail.url.replace(urlPrefix + appName, '')])

  7. }

相似的,当我们需要从应用 A 跳转到应用 B 时,我们也需要这样的一个机制:

 
  1. window.addEventListener('mooa.routing.navigate', function(event: CustomEvent) {

  2.  const opts = event.detail

  3.  if (opts) {

  4.  navigateAppByName(opts)

  5.  }

  6. })

剩下的诸如 Loading 动画也是类似的。

使用 Mooa 进行

So,我们就有了前端微服务框架 Mooa。它基于 single-spa && single-spa-angular-cli,并且符合以上的设计思想。

GayHub 地址:https://github.com/phodal/mooa

对于主工程而言,只需要以下的几行代码就可以完成上面的功能:

 
  1. http.get<any[]>('/assets/apps.json')

  2.  .subscribe(data => {

  3.  data.map((config) => {

  4.  that.mooa.registerApplication(config.name, config, mooaRouter.matchRoute(config.prefix));

  5.  });

  6.  this.mooa.start();

  7.  });

  8. this.router.events.subscribe((event: any) => {

  9.  if (event instanceof NavigationEnd) {

  10.  that.mooa.reRouter(event);

  11.  }

  12. });

并添加一个对应的子应用路由:

 
  1. {

  2.  path: 'app/:appName/:route',

  3.  component: HomeComponent

  4. }

则如上所述的四个步骤。

对于子工程而言,则只需要一个对应的 Hook 操作:

 
  1. mooaPlatform.mount('help').then((opts) => {

  2.  platformBrowserDynamic().bootstrapModule(AppModule).then((module) => {

  3.  opts['attachUnmount'](module);

  4.  });

  5. });

并设置好对应的 base_href:

 
  1. providers: [

  2.  {provide: APP_BASE_HREF, useValue: mooaPlatform.appBase()},

  3. ]

嗯,就是这么简单。DEMO 视频如下:

Demo 地址见:http://mooa.phodal.com/

GitHub 示例:https://github.com/phodal/mooa


原文发布时间:2018年03月18日

作者:phodal_

本文来源:CSDN  如需转载请联系原作者

目录
相关文章
|
11天前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
16天前
|
XML 前端开发 JavaScript
前端技术的演变与实战应用
前端技术的演变与实战应用
|
11天前
|
Dart 前端开发 开发工具
【Flutter前端技术开发专栏】Flutter入门指南:搭建开发环境与第一个应用
【4月更文挑战第30天】本文介绍了Flutter SDK的安装和配置过程,以及如何创建并运行第一个Flutter应用。首先确保安装了Dart SDK和Flutter SDK,支持macOS、Linux和Windows。安装完成后,设置环境变量,然后通过`flutter doctor`验证安装。接着,使用`flutter create`命令创建新项目,进入项目目录并运行`flutter run`启动应用。在`main.dart`中修改代码以自定义应用。Flutter支持热重载和DevTools调试。本文为Flutter初学者提供了快速入门的指导。
【Flutter前端技术开发专栏】Flutter入门指南:搭建开发环境与第一个应用
|
4天前
|
JSON 前端开发 JavaScript
快照测试在前端自动化测试中的应用
在前端自动化测试中,快照测试常用于检验组件渲染与布局。
|
10天前
|
缓存 移动开发 前端开发
【专栏:HTML与CSS前端技术趋势篇】HTML与CSS在PWA(Progressive Web Apps)中的应用
【4月更文挑战第30天】PWA(Progressive Web Apps)结合现代Web技术,提供接近原生应用的体验。HTML在PWA中构建页面结构和内容,响应式设计、语义化标签、Manifest文件和离线页面的创建都离不开HTML。CSS则用于定制主题样式、实现动画效果、响应式布局和管理字体图标。两者协同工作,保证PWA在不同设备和网络环境下的快速、可靠和一致性体验。随着前端技术进步,HTML与CSS在PWA中的应用将更广泛。
|
10天前
|
前端开发 JavaScript 搜索推荐
【专栏:HTML 与 CSS 前端技术趋势篇】HTML 与 CSS 在 Web 组件化中的应用
【4月更文挑战第30天】本文探讨了HTML和CSS在Web组件化中的应用及其在前端趋势中的重要性。组件化提高了代码复用、维护性和扩展性。HTML提供组件结构,语义化标签增进可读性,支持用户交互;CSS实现样式封装、布局控制和主题定制。案例展示了导航栏、卡片和模态框组件的创建。响应式设计、动态样式、CSS预处理器和Web组件标准等趋势影响HTML/CSS在组件化中的应用。面对兼容性、代码复杂度和性能优化挑战,需采取相应策略。未来,持续发掘HTML和CSS潜力,推动组件化开发创新,提升Web应用体验。
|
10天前
|
前端开发 持续交付 开发工具
【专栏:工具与技巧篇】版本控制与Git在前端开发中的应用
【4月更文挑战第30天】Git是前端开发中的必备工具,它通过分布式版本控制管理代码历史,支持分支、合并、回滚等操作,促进团队协作和冲突解决。在前端项目中,Git用于代码追踪、代码审查、持续集成与部署,提升效率和质量。优化协作包括制定分支策略、编写清晰提交信息、定期合并清理分支及使用Git钩子和自动化工具。掌握Git能有效提升开发效率和代码质量。
|
11天前
|
前端开发 JavaScript 安全
【TypeScript技术专栏】TypeScript在微前端架构中的应用
【4月更文挑战第30天】微前端架构通过拆分应用提升开发效率和降低维护成本,TypeScript作为静态类型语言,以其类型安全、代码智能提示和重构支持强化这一架构。在实践中,TypeScript定义公共接口确保跨微前端通信一致性,用于编写微前端以保证代码质量,且能无缝集成到构建流程中。在微前端架构中,TypeScript是保障正确性和可维护性的有力工具。
|
11天前
|
缓存 监控 前端开发
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
【4月更文挑战第30天】本文探讨了Flutter应用的性能调优策略和测试方法。性能调优对提升用户体验、降低能耗和增强稳定性至关重要。优化布局(避免复杂嵌套,使用`const`构造函数)、管理内存、优化动画、实现懒加载和按需加载,以及利用Flutter的性能工具(如DevTools)都是有效的调优手段。性能测试包括基准测试、性能分析、压力测试和电池效率测试。文中还以ListView为例,展示了如何实践这些优化技巧。持续的性能调优是提升Flutter应用质量的关键。
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
|
11天前
|
前端开发 Android开发 开发者
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发
【4月更文挑战第30天】本文探讨了使用Flutter开发混合应用的方法。混合应用结合Web技术和原生容器,提供快速开发和低成本维护。Flutter,一款现代前端框架,以其插件系统和高性能渲染引擎支持混合应用开发。通过创建Flutter项目、添加平台代码、使用WebView、处理平台间通信以及发布应用,开发者可构建跨平台混合应用。虽然混合应用有性能和用户体验的局限,但Flutter的跨平台兼容性和丰富的插件生态降低了开发成本。开发者应根据项目需求权衡选择。
【Flutter前端技术开发专栏】Flutter中的混合应用(Hybrid Apps)开发