写点什么

基于 Angular 的微前端理念与实践

  • 2022-10-31
    北京
  • 本文字数:4569 字

    阅读完需:约 15 分钟

基于Angular的微前端理念与实践

现代Web应用正在变得越来越庞大和复杂,有时候这样的应用会由不同的团队来管理。应用可能会包含不同团队开发的特性,在交付整个应用之前,我们可能希望只将某些特定的功能发布到生产环境中。如果整个应用只有一个仓库(repo),那我们该如何管理不同的团队和不同的发布周期呢?

 

这些复杂的应用大多位于客户端,使其更加难以维护。这种单体式的臃肿应用还有一些其他的问题。在本文中,我将会讨论微前端的优势、劣势、实现方式以及其他的内容。

简介

 

微前端是一些小型的应用,大多会根据子域或功能进行划分,它们互相协作来交付一个更大的应用。在深入介绍微前端的实现之前,我们将会阐述什么是微前端以及为什么要使用它。

 

通常,项目都有不同的规模和不同的需求。如果你的项目非常简单,只有两三个页面,那么根本没有必要考虑微前端。你可以直接使用自己选择的任意框架来实现,比如Angular、React 或 Vuejs。

 

但是,事实并非总是如此。有时候,你的前端应用是另一个大型应用的一小部分,或者你的应用有很多的区域和特性组成,它们由不同的团队进行开发,又或者你的应用要按特性依次发布到生产环境中。如果你正在面临这样的场景,那么就需要考虑一下微前端了。我们看一下这张图片。



如上图所示,我们有 6 个前端应用互相协作来交付一个更大的应用。这些应用之间的通信可以借助事件总线、window 对象或发布/订阅方法来实现。每个应用都可以由不同的团队和任意框架实现。每个应用都可以独立地与其后端或端点进行交互。这里有一个 bootstrap/launch 应用,它会负责加载所有其他的应用,并根据用户的交互或路由在 DOM 中挂载或卸载它们。

 

这种微前端架构主要有如下的优势。

 

  • 应用会很小:显然,当大的应用按照区域、页面或特性进行拆分后,每个应用都会变得很小。

  • 应用是独立的:由于所有的应用都是单独拆分和开发的,所以它们是相互独立的。

  • 应用更易于理解:因为每个应用更小,由单一团队进行开发,所以更易于理解。

  • 应用更易于开发和部署:由于这些应用本身都很小,都由单一的团队进行开发,所以很易于开发和部署。我们甚至可以独立部署它们。

  • 应用更易于测试:我们必须为大型的应用编写成千上万的单元测试,并且需要一直运行。这会拖慢我们的部署过程。在实现微前端之后,每个应用都有数量更少的单元测试,并且可以独立运行自己的单元测试。

  • 应用的开发会更迅速:因为应用都有独立的团队,所以整个开发会更迅速、更容易。

  • CI/CD 会更简单:每个应用都可以单独集成和部署,这使得 CI/CD 过程会变得更加容易。当我们修复某个应用或者引入新的特性时,不用考虑整个应用的情况,因为所有的特性都是独立的。

  • 独立的技术栈和版本:我们可以为每个应用选择自己的技术栈,只不过这种情况不太多见。但是,我们可以使用相同技术栈的不同版本。例如,有些团队可能有足够的灵活性和时间来引入和测试同一技术栈的较新版本。

  • 没有共享的代码:在大型的应用中,我们倾向于跨特性共享代码,但是,这并不能很好地进行扩展,而且随着应用越来越大,会引入很多缺陷和相互依赖。微前端中则没有这样的问题,因为我们不会共享代码,除非它是一个哑(dumb)组件。

  • 能够很容易地在不影响旧有架构的情况下变更架构:有时候,我们必须要扩展旧的架构,但是可能没有足够的开发人员来实现或扩展架构。借助微前端的方式,我们可以使用最新的技术栈开发新特性,并独立进行交付。

微前端的特点

 

  • 每个前端应用代表整个应用的一个特定功能或子域。

  • 每个前端应用都可以由一个独立的团队来实现。

  • 每个前端应用可以采用不同的技术来实现。

  • 它们之间不能共享逻辑,而且相互独立。

  • 每个前端应用都由一个团队来负责。

如何拆分应用

 

我们看一下如何将大型应用拆分为微前端。在这方面,没有拆分应用的具体标准,我们可以根据自己的需要以多种方式进行拆分。我们会看到各种拆分应用的方式。

按照特性

 

这是最常见的方法,因为我们可以很容易地划分应用的功能。例如,如果应用有三个特性,分别是 Dashboard、Profile 和 Views,我们可以将每个特性作为一个单独的应用,并在 Launch.js 的辅助下在 DOM 中挂载和卸载它们。这个 Launch.js 可以是一个独立的应用,也可以只是一个简单的 JavaScript 应用。


按照区域

 

在有些应用中,每个区域都有很多功能,例如,在 coinbase、Gmail 中。在这种情况下,我们可以将每个区域作为一个新的应用来实现。


按页面

 

有些应用的功能是按页面划分的。每个页面都有一些独立的功能。我们可以通过页面来划分应用。在下图中,我们有四个页面,可以分别创建四个应用。

 

按照域

 

基于域来拆分应用也是最常见的方式之一。


微前端的不同实现方式

 

我们有很多实现微前端的方式,我发现最常用的是如下 6 种:

 

  • Iframes

  • 借助 NGINX

  • Web Component/Angular 元素

  • Angular 库

  • Monorepos

  • 定制化的编排器

微前端框架

 

微前端出现至少已经有两年了,但它依然是一个新兴领域。你可能会问有没有相关的框架或库帮助我们实现这种架构,从而减轻我们工作。答案是肯定的,目前已经有一些相关的库或框架了。


single-spa

 

single-spa 是一个用于前端微服务的 JavaScript 框架,可以用最流行的三个框架/库来实现,即 Angular、React 和 Vue.js。它可以根据需要懒加载应用,请查阅他们的网站以了解更多信息。

frint.js

 

frint.js 是一个模块化的 JavaScript 框架,用于构建可扩展和反应式的应用。目前,它不支持 Angular,但支持 React。如果你要从头开始构建一个反应式应用,而且刚刚开始的话,这个框架会特别适合你。请参阅他们的网站以了解更多信息。

使用 Angular 的微前端项目实例

 

有了这些基础知识之后,我们在single-spa框架的协助下构建一个 Angular 项目的样例,我希望构建一个简单的应用以便于演示。

 

我们将按下图所示,把这个应用分成多个组成部分。我们一共要实现 4 个应用,分别是 HeaderApp、DashboardApp、FooterApp 和根应用。

 


如下是四个应用的代码仓库,你可以在自己的机器上分别克隆并运行它们。

 

// root app runs on port 4200git clone https://github.com/ahmedbhl/micro-root.gitnpm installnpm start// micro header runs on port 4300git clone https://github.com/ahmedbhl/micro-header.gitnpm installnpm start// micro dashboard runs on port 4202git clone https://github.com/ahmedbhl/micro-dashboard.gitnpm installnpm start// micro footer runs on port 4201git clone https://github.com/ahmedbhl/micro-footer.gitnpm installnpm start
复制代码

 

然后,可以在 http://localhost:4200/ 上访问整个应用程序

 


如下是根应用的 index HTML 文件。我们在第 10 行导入了这三个应用,并以适当的名称和位置注册了这些应用。由于我们在页面加载时加载了所有的应用程序,所以没有定义任何特定的上下文路径。

 

<!DOCTYPE html><html>  <head>    <meta http-equiv="Content-Security-Policy" content="default-src *  data: blob: 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';">    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <title>Your application</title>    <meta name="viewport" content="width=device-width, initial-scale=1">    <meta name="importmap-type" content="systemjs-importmap">    <script type="systemjs-importmap">      {        "imports": {          "footer": "http://localhost:4201/main.js",          "dashboard": "http://localhost:4202/main.js",          "header": "http://localhost:4300/main.js",          "single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.5/system/single-spa.min.js"        }      }    </script>    <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.5/system/single-spa.min.js" as="script" crossorigin="anonymous" />    <script src='https://unpkg.com/core-js-bundle@3.1.4/minified.js'></script>    <script src="https://unpkg.com/zone.js"></script>    <script src="https://unpkg.com/import-map-overrides@1.6.0/dist/import-map-overrides.js"></script>    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/system.min.js"></script>    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/amd.min.js"></script>    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/named-exports.js"></script>    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/named-register.min.js"></script>    <style>    </style>  </head>  <body>    <script>      System.import('single-spa').then(function (singleSpa) {        singleSpa.registerApplication(          'header',          function () {            return System.import('header');          },          function (location) {            return true;          }        )        singleSpa.registerApplication(          'dashboard',          function () {            return System.import('dashboard');          },          function (location) {            // return location.pathname.startsWith('/app2');            return true;          }        )        singleSpa.registerApplication(          'footer',          function () {            return System.import('footer');          },          function (location) {            // return location.pathname.startsWith('/app1');            return true;          }        );        singleSpa.start();      })    </script>    <import-map-overrides-full></import-map-overrides-full>  </body></html>
复制代码

 

我们可以设置“/header”的位置路径,这样当浏览器的 URL 导航到“/header”时就会加载 header。我们来测试一下。

 

<script>      System.import('single-spa').then(function (singleSpa) {        singleSpa.registerApplication(          'header',          function () {            return System.import('header');          },          function (location) {            return location.pathname.startsWith('/header');            // return true;          }        )
复制代码

 

总结

 

我知道微前端是一个很时尚的东西,但你不应该在每个应用中都使用它。如果你的应用程序很小,就没有必要这样做,不要把事情复杂化。这种方式的目的是让我们的整个过程更加顺畅,而不是增加复杂性。所以在使用该方式之前,先要进行必要的判断。


原文链接:

https://blog.devgenius.io/angular-micro-frontend-4dad619c4277


相关阅读:

微前端如何改变 Angular 的未来?

Angular 13 发布:全面弃用 View Engine

Angular、React 和 Vue 三大框架,Web 开发该如何选择?

2022-10-31 23:243818

评论

发布
暂无评论
发现更多内容

微博系统“微博评论”高性能高可用计算架构

张立奎

Linux 黑客命令装逼小助手

CTO技术共享

开源一夏 | Python 对象的序列和反序列化

宇宙之一粟

Python 开源 序列化 8月月更

leetcode 594. Longest Harmonious Subsequence 最长和谐子序列(简单).md

okokabcd

LeetCode 算法与数据结构

【数据结构实践】手把手带你实现 Python 自定义数组

迷彩

开源 数据结构 面向对象 数组操作 8月月更

Docker下Prometheus和Grafana三部曲之二:细说Docker编排

程序员欣宸

Grafana Prometheus 8月月更

新元联手倍市得,以数字化手段实现人才公租房项目满意度持续监测

科技怪咖

开源一夏 | Qiankun框架对于微前端的解耦和沙盒与实战探索心得

恒山其若陋兮

开源 8月月更

Java并发面试常见考点

浅羽技术

Java 面试 线程 并发 8月月更

4.0 SDK Workshop 纪实:一起体验多人、多屏幕共享新功能

声网

人工智能 音视频

计算机接口技术复习题(1-6章)

乌龟哥哥

8月月更

Python 教程之输入输出(9)—— print() 中的 sep 参数

海拥(haiyong.site)

Python 8月月更

头脑风暴:最长回文子序列

HelloWorld杰少

LeetCode 8月月更

Dockerfile 定制专属镜像

CTO技术共享

【JavaScript】:有关js类型转换的那些事...

翼同学

JavaScript 编程语言、 8月月更 学习分享

数据库中存媒体文件的字段用什么类型?一文带你了解二进制大对象BLOB

wljslmz

数据库 8月月更

豆瓣 TOP3 的 Python 书,千万别错过

图灵教育

【Java·访问修饰符】:default、public、protected、private

翼同学

Java 学习 编程语言 8月月更

面试中常用消息中间件对比

浅羽技术

kafka RocketMQ 消息中间件 Rabbit MQ 8月月更

开源一夏 | Python 并发编程之死锁

宇宙之一粟

Python 开源 并发编程 死锁 8月月更

老板问我要ROI,我让他先挑宽门or窄门

科技怪咖

看准六点,帮你选对客户体验管理(CEM)系统

科技怪咖

合成资产赛道风云突变,Linear Finance有望成为最具潜力的黑马

鳄鱼视界

数据点按时间间隔以及数据值分割数据块

waitmoon

算法 SLO

Docker 已运行端口映射怎么破

CTO技术共享

【CSS】字体样式,包括字体系列、大小、修饰、粗细、简写...

翼同学

CSS 编程语言 8月月更

悲观锁和乐观锁的区别以及实现方式

浅羽技术

Java 面试 面试题 秋招 8月月更

精细化资产管理

IT资讯搬运工

【数据结构实践】从0到1带你利用Python实现自定义集合

迷彩

数据结构 集合运算 8月月更 自定义集合

每日一R「14」错误处理

Samson

学习笔记 8月月更 ​Rust

从事DevOps工作应该掌握哪些语言及工具

穿过生命散发芬芳

DevOps 8月月更

基于Angular的微前端理念与实践_大前端_Ahmed Bouhlel_InfoQ精选文章