AI实践哪家强?来 AICon, 解锁技术前沿,探寻产业新机! 了解详情
写点什么

基于 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:244062

评论

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

模块分解 - 微服务架构认识与思考

raox

“复制”马斯克(一):全世界都会为“自大狂”让路吗?

脑极体

创业失败启示录|雨季后的明媚阳光

阿萌

28天写作 创业失败启示录

我们都很忙

Ian哥

28天写作

nodejs的调试debug

程序那些事

debug 调试 nodejs 程序那些事 程序调试

记录关于写作的两个小想法

JiangX

28天写作

淘宝网前期技术架构演进分析

Andy

Soul 源码阅读 05|Http 长轮询同步数据分析

哼干嘛

WebPack | Loader处理非JavaScript模块机制详解

梁龙先森

JavaScript 大前端 webpack 28天写作

原来酸奶是这个味道,真香!「幻想短篇 20/28」

道伟

28天写作

GNUCash 5: 报表

lidaobing

GNUCash 28天写作

如何养成一个好习惯

熊斌

读书笔记 28天写作

技术根儿扎得深,不怕“首都”狂风吹!

鲁米

操作系统

9个REST API设计的基本准则

devpoint

APi设计 RESTf

28天瞎写的第二百三十天:DIY 键盘的故事

树上

28天写作 机械键盘 GH60

壁纸收藏

小马哥

摄影 七日更

机器学习·笔记之:Cost Function - Intuition I

Nydia

一文搞懂“技术债”

李忠良

28天写作

产品训练营-第五课

Geek_娴子

开发质量提升系列:用户体验

罗小龙

最佳实践 方法论 28天写作

HDFS是如何设计架构的?

大数据老哥

Java基础--2021Java面试题系列教程--大白话解读

JavaPub

Java 面试 javapub

进大厂必看!拼多多大佬总结的10万字Springboot经典学习笔记

武哥聊编程

Java springboot 28天写作

精彩的封面 | 视频号 28 天(20)

赵新龙

28天写作

CSS(六)——用CSS设置图像效果

程序员的时光

程序员 大前端 七日更 28天写作

关于垃圾收集器你了解多少?一文总结七大垃圾收集器

Java鱼仔

Java JVM 垃圾收集

浪漫主义的消亡

石君

28天写作

年底跳槽之 如何找工作方向?

一笑

职业规划 28天写作

工程师思维是什么?能吃吗?

Justin

工程师思维 架构设计 28天写作

架构师训练营知识点思维导图

晴空万里

架构师训练营第2期

个人隐私之老话重谈

张老蔫

28天写作

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