1、什么是微前端
好的前端开发很难。扩展前端开发以便许多团队可以同时在大型复杂产品上工作更加困难。
什么是微前端? Dan 大神是这么说的:😭😭😭😭😭😭😭😭😭😭
Micro Frontends一词于 2016 年底首次出现在ThoughtWorks Technology Radar中:
We've seen significant benefit from introducing microservice architectures, which have allowed teams to scale delivery of independently deployed and maintained services. However, teams have often struggled to avoid the creation of front-end monoliths—large and sprawling browser applications that are as difficult to maintain and evolve as the monolithic server-side applications we've abandoned. We're seeing an approach emerge that our teams call micro frontends. In this approach, a web application is broken up by its pages and features, with each feature being owned end-to-end by a single team. Multiple techniques exist to bring the application features—some old and some new—together as a cohesive user experience, but the goal remains to allow each feature to be developed, tested and deployed independently from others. The BFF - backend for frontends approach works well here, with each team developing a BFF to support its set of application features.
我们已经看到引入微服务架构的显着好处,它允许团队扩展独立部署和维护的服务的交付。然而,团队经常努力避免创建前端单体——庞大而庞大的浏览器应用程序,它们与我们放弃的单体服务器端应用程序一样难以维护和发展。我们看到一种方法出现了,我们的团队称之为微前端. 在这种方法中,Web 应用程序被其页面和功能分解,每个功能都由一个团队端到端拥有。存在多种技术来将应用程序功能(一些旧的和一些新的)结合在一起作为一种有凝聚力的用户体验,但目标仍然是允许每个功能独立于其他功能进行开发、测试和部署。BFF - 前端方法的后端在这里很有效,每个团队开发一个 BFF 来支持其应用程序功能集。
—— 2016年11月 from ThoughtWorks Technology Radar
1.1 微前端简介
近年来,微服务大受欢迎,许多组织使用这种架构风格来避免大型单体后端的局限性。虽然关于这种构建服务器端软件的风格已经写了很多,但许多公司仍在与单一的前端代码库作斗争。
现代的前端应用的发展趋势正在变得越来越富功能化,富交互化。复杂的单体前端应用背后则是数量庞大的后端应用组成的微服务集群。在一个团队中维护的前端项目,随着时间推进,会变得越来越庞大,越来越难以维护。所以我们给这种应用起名为巨石单体应用。
也许你想构建一个渐进式或响应式 Web
应用程序,但找不到一个容易的地方开始将这些功能集成到现有代码中。也许你想开始使用新的 JavaScript
语言功能(或可以编译为 JavaScript
的无数语言之一),但你无法将必要的构建工具安装到现有的构建过程中。或者,也许你只是想扩展你的开发,以便多个团队可以同时在一个产品上工作,但现有单体中的耦合和复杂性意味着每个人都在踩着彼此的脚趾。这些都是真正的问题,都会对你有效地为客户提供高质量体验的能力产生负面影响。
最近几年,我们看到越来越多的注意力集中在复杂的现代 Web 开发所必需的整体架构和组织结构上。特别是,我们看到出现了将前端整体分解为更小、更简单的块的模式,这些块可以独立开发、测试和部署,同时仍然作为一个单一的内聚产品出现在客户面前。我们将这种技术称为 微前端,我们将其定义为:
“一种将可独立交付的前端应用程序组合成一个更大整体的架构风格”
它将微服务的概念扩展到前端世界。当前的趋势是构建一个功能丰富且功能强大的浏览器应用程序,也就是位于微服务架构之上的单页应用程序。随着时间的推移,通常由独立团队开发的前端层会增长并变得更难维护。这就是我们所说的Frontend Monolith。
然而,这个想法并不新鲜。它与独立系统概念有很多共同点。在过去,这样的方法被称为垂直化系统的前端集成。但微前端显然是一个更友好、更简洁的术语。
1.1.1 微服务 + 单体前端
Monolith
有一个团队来创建和维护一个完整的应用程序,共享数据库、后端和前端。Front & Back
将整体式应用程序一分为二,也将工作团队划分为拥有其整体式后端或前端。
借助微服务,后端架构演变为更具可扩展性的架构,因为每个微服务都属于不同的工作团队。虽然后端的划分对用户是透明的,但是当试图将微服务集成到单体前端时就会出现问题,成为应用程序的瓶颈,单体系统的缺点是:
- 太大太复杂,任何人都无法完全理解并快速正确地进行更改。
- 前端代码的更改可能会影响整个网站。
- 前端代码的任何修改都必须重新实现,增加了编译时间。
1.1.2 微前端
直到出现了垂直组织,通过微前端,使得架构变得不那么复杂,微前端将应用程序分成小的独立功能,每个功能由一个工作团队从后端到前端同时实现。
由于单体前端存在的问题,这种模式变得流行起来。随着前端的迅速发展,继续采用单体架构,维护起来变得更愈发困难。使用微前端,可以保证与后端微服务架构相同的可扩展性、灵活性和适应性。创建的应用程序不那么繁琐并且更加用户友好。而且,每个微前端都可以使用不同的框架进行开发。
1.1.3 微前端的核心思想
微前端是一种前端架构模式,是一种将前端应用程序拆分为更小、更独立的部分,并使用不同的技术栈来构建和部署这些部分的方法。这些独立的部分可以是单个页面、组件、功能模块或应用程序。这种方法的目的是使团队能够更轻松地开发、测试、部署和维护前端应用程序,每个部分都可以使用不同的技术栈和独立的团队进行开发,这样可以更好地满足团队的需求和能力,同时还能够实现更高的可扩展性和灵活性。
微前端的出现背景主要有以下的几点:
- 应用程序复杂性的增加:在过去,前端应用程序通常是一个单一的代码库,由一个开发团队维护。随着应用程序的复杂性增加,开发和维护单一的代码库变得越来越困难。微前端可以帮助团队将应用程序拆分为更小、更易于管理的部分,从而降低应用程序复杂性。
- 技术栈多样性:前端技术栈在不断发展和演变,有时会出现多种不同的技术栈。这可能会导致团队在选择技术栈时出现争议。微前端可以允许团队使用不同的技术栈来构建和部署应用程序的不同部分,从而解决这个问题。
- 团队规模的增加:随着团队规模的增加,单一的代码库可能会变得不够灵活和可扩展。微前端可以帮助团队将应用程序拆分为更小、更容易管理的部分,并允许不同的团队在不同的部分上工作,从而提高整个团队的效率。
- 高可用性和容错性:微前端可以帮助团队将应用程序拆分为更小、更容易管理的部分,从而降低应用程序崩溃的风险。当一个部分出现问题时,其他部分可以继续运行,从而提高应用程序的可用性和容错性。
简单来说:微前端是一种架构风格,将独立交付的前端应用程序组合成一个更大的整体。保证产品体验的同时提升开发体验。
微前端的核心思想主要有:
- 技术独立::每个工作团队都可以自由采用其选择的技术堆栈,而无需与其他团队协调。
- 每个团队的应用程序代码都是隔离的::不同工作团队之间不共享任何运行时,即使他们使用相同的框架。因此,应用程序中没有全局变量或共享状态。
- 健壮的网页设计::应用程序功能必须可用。“通用渲染”和“渐进增强”方法可用于提高性能。
- 与技术无关:每个团队都应该能够选择和升级他们的堆栈,而无需与其他团队协调。自定义元素是隐藏实现细节同时为其他人提供中性界面的好方法。
- 隔离团队代码:不要共享运行时,即使所有团队都使用相同的框架。构建自包含的独立应用程序。不要依赖共享状态或全局变量。
- 建立团队前缀:就尚无法隔离的命名约定达成一致。命名空间
CSS
、事件、本地存储和Cookie
,以避免冲突并明确所有权。 - 比自定义 API 更喜欢本机浏览器功能:使用浏览器事件进行通信,而不是构建全球 PubSub 系统。如果您真的必须构建跨团队 API,请尽量保持简单。
- 构建一个弹性站点:你的功能应该很有用,即使 JavaScript 代码执行失败或尚未执行。使用通用渲染和渐进增强来提高感知性能。
1.2 微前端的优势
1.2.1 粒度较小
- 业务级或更小的粒度,一个业务团队可以独立管理自己的业务代码,更容易维护,同时方便独立建立 CI/CD 流水线,独立部署。
- 而不是像单体应用是一个庞大的代码库。
1.2.2 解耦
- 业务之间低耦合,互不影响,方便升级,更新,重构/重写,尝试不同的技术。
- 业务内部功能高内聚,业务纵向分离,独立自治。
- 而不是像单体应用那样各个业务放在一起,大家用统一的技术栈,共用一个开发部署流程。
1.2.3 灵活、可扩展和可维护
- 以前两点为前提,微前端架构更容易增加新的业务或扩展已有的业务,方便多个把业务的旧代码慢慢转换成微前端。
- 灵活性:微前端可以将前端应用程序拆分为更小、更易于管理的部分,使得团队可以更加灵活地开发、测试、部署和维护前端应用程序。
- 可扩展性:微前端可以帮助团队将前端应用程序拆分为多个部分,每个部分可以独立地扩展,从而使应用程序具有更高的可扩展性。
- 可维护性:微前端可以将前端应用程序拆分为更小、更易于管理的部分,使得团队可以更加容易地维护前端应用程序。
- 技术栈多样性:微前端可以允许团队使用不同的技术栈来构建和部署应用程序的不同部分,从而使得团队可以选择最适合自己的技术栈,提高开发效率和可用性。
- 高可用性和容错性:微前端可以降低应用程序崩溃的风险。当一个部分出现问题时,其他部分可以继续运行,从而提高应用程序的可用性和容错性。
1.3 微前端带来的问题
- 每个微前端单独打包会导致一些重复的依赖包,比如多个微前端使用了
React
,那么每个包都有重复的React
,而且不方便把他们提取出来,因为每个微前端可能需要不同版本的依赖包。更大的bundle size
导致用户需要下载的资源变大,对应用的性能和用户体验有不好的影响。 - 微服务和微前端都会无法避免的增加复杂度,有更多的代码仓库、工具、流水线和服务器需要管理。所以在选择微前端架构之前要考虑好有没有足够的自动化流程和基础设施支持,有没有扩展他们的能力,有没有保证各微前端质量和一致性方案。
1.4 微前端的使用场景
- 大型单页应用程序:在一个单一的代码库中开发和维护一个大型单页应用程序可能会变得非常复杂,使用微前端可以将应用程序拆分为更小、更易于管理的部分,从而提高可维护性和可扩展性。
- 复杂的应用程序:当应用程序变得越来越复杂时,使用微前端可以降低应用程序的复杂性,并提高开发效率和可用性。
- 多个团队合作开发:当多个团队共同开发一个应用程序时,使用微前端可以将应用程序拆分为多个部分,每个团队可以独立地开发和部署自己的部分,从而提高整个团队的效率。
- 多个技术栈的应用程序:当应用程序需要使用不同的技术栈时,使用微前端可以让团队选择最适合自己的技术栈,提高开发效率和可用性。
总的来说,微前端适用于各种规模和类型的前端应用程序,特别是大型、复杂的应用程序和多人合作开发的应用程序。
2、几个重要的概念
集成微前端的难点主要有三个地方:CSS 隔离, JS 隔离,和通信。
2.1 应用隔离
应用隔离主要分两种情况:
- 主应用与子应用之间的隔离;
- 子应用与子应用之间的隔离;
应用间所隔离的主要是 Javascript 的沙箱隔离 和 CSS 的样式隔离。
2.1.1 Javascript 沙箱隔离
在微前端的场景,由于多个独立的应用被组织到了一起,在没有类似 iframe
的原生隔离下,势必会出现冲突,如全局变量冲突、样式冲突,这些冲突可能会导致应用样式异常,甚至功能不可用。通常,子应用在运行期间会有一些污染性的副作用产生,比如全局变量、全局事件、定时器、网络请求、localStorage
、全局 Style 样式
、全局 DOM 元素
等。
所以想让微前端达到生产可用的程度,保证应用能够稳定的运行且互不影响,需要提供安全的运行环境,能够有效地隔离、收集、清除应用在运行期间所产生的副作用,让每个子应用之间达到一定程度隔离的沙箱机制是必不可少的,这就是沙箱的设计目标。
JS隔离的方式主要有两种,一种是快照拷贝的方式,一个是基于
proxy
的方式。qiankun
优先使用proxy 沙盒
,如果浏览器不支持proxy
,那么再使用快照沙盒。
1. 快照沙箱 - snapshotSandbox
在创建微应用的时候会实例化一个沙盒对象,它有两个方法,active
是在激活微应用的时候执行,而 inactive
是在离开微应用的时候执行。
整体的思路是在激活微应用时将当前的 window对象
拷贝存起来,然后从 modifyPropsMap
中恢复这个微应用上次修改的属性到 window
中。在离开微应用时会与原有的 window对象
做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。
2. 代理沙箱 - proxySandbox
微应用中的 script
内容都会加 with(global)
来执行,这里 global
是全局对象,如果是proxy的隔离方式那么他就是下面新创建的proxy对象。
我们知道 with
可以改变里面代码的作用域,也就是我们的微应用全局对象会变成下面的这个 proxy
。当设置属性的时候会设置到 proxy对象
里,在读取属性时先从 proxy
里找,没找到再从原始的 window
中找。也就是你在微应用里修改全局对象的属性时不会在 window
中修改,而是在 proxy对象
中修改。因为不会破坏 window对象
,这样就会隔离各个应用之间的数据影响。
详细的可看:微前端框架Qiankun 沙箱原理 - Origin of Ray
2.1.2 CSS 样式隔离
由于在微前端场景下,不同技术栈的子应用会被集成到同一个运行池中,所以我们必须在框架层确保各个子主应用之间不会出现样式互相干扰的问题。
1. 常见的解决方案
- 严格的命名约定,例如
BEM
; CSS Module
;- 各种
CSS-in-JS
库; shadow DOM
;
微前端的样式问题也可以通过团队间商量好 CSS
命名规则约定,然后结合使用 CSS
预处理器比如 SASS
,其中 selector nesting
功能可以借用来当作 CSS namespace
。这样的坏处是难以把已有的大型单体应用中的业务模块慢慢转化成微前端,因为它们比较难以进行大幅度修改。
CSS in JS 是一个比较好的解决方法,因为样式和它对应的组件绑在一起,修改一个组件的样式不会影响其他任何地方,如果删除这个微前端(比如界面上切换了菜单/页面),微前端的 CSS
也会跟着被删除,所以不用担心样式冲突了。
2. qiankun 样式隔离方案
在 css文件
隔离上,qiankun
提供了两种样式隔离的功能:严格样式隔离和 scoped
样式隔离。
- 严格样式隔离
- 严格样式隔离,默认情况下是关闭的。如果需要开启,必须显示配置。
- 严格样式隔离,是基于 Web Component 的 shadow Dom 实现的。通过 shadow Dom, 我们可以将一个隐藏的、独立的 dom 附加到一个另一个 dom 元素上,保证元素的私有化,不用担心与文档的其他部分发生冲突。
scoped
样式隔离
scoped
样式隔离,是基于属性选择器实现的,类似div["data-qiankun=react"]
html entry
解析以后的 html
模板字符串,再添加到 container
指定的节点之前,会先包裹一层 div
,并且为这个 div
节点添加 data-qian
属性,属性值为子应用的 name 属性
;然后遍历 html
模板字符串中所有的 style
节点,依次为内部样式表中的样式添加 div["data-qiankun=xxx"]
前缀。qiankun
中子应用的 name
属性值是唯一的,这样通过属性选择器的限制,就可实现样式隔离。
严格样式隔离和 scoped
样式隔离不能同时使用,当两者对应的配置项都为 true
时,严格样式隔离的优先级更高。
2.2 应用通信
微前端最常见的问题之一是如何让微应用之间能够相互通信。虽然组成宏应用程序的微应用程序根据定义是松散耦合的,但它们仍然需要能够相互通信。例如,一个导航微应用需要发出一个通知,通知用户刚刚选择的某个其他微应用应该被激活,而要被激活的应用需要接收这样的通知。
根据我们的极简主义思维方式,我们希望避免引入大量消息传递机制。相反,本着 Web 组件的精神,我们将使用 DOM 事件。我们提供了一个简单的广播 API,它预先通知所有存根即将发生的事件,等待任何已请求激活的事件类型被激活,然后针对文档分派事件,任何微应用程序都可以在文档上监听它。鉴于我们所有的iframes
都是同源的,我们可以从页面到达iframe
页面,反之亦然,以找到触发事件的元素。
一般而言,我们建议让微应用之间尽可能少地通信,因为这通常会重新引入我们最初试图避免的那种不适当的耦合代码。也就是说,通常我们只需要某种程度的跨应用通信即可。通常使用浏览器自带的 events 比如 click
, select
, mouseover
实现通信,如果有更多的需求可以使用 Custom event ,这样直接使用浏览器的 API 可以让微前端架构保持解耦。如果不使用浏览器的API,我们也可以写一个 JS 库实现全局的事件驱动式的通信管理功能,但会加入一定程度的耦合。
2.2.1 常见的通信方式
- 使用自定义事件通信,是降低耦合的一种好方法;
- 可以考虑
React 或 Vue
应用中常见的全局state store
机制; - 发布-订阅(pub/sub)模式的通信机制;
- 使用 地址栏作为通信机制;
2.2.2 qiankun - Actions 通信
qiankun
内部提供了 initGlobalState
方法用于注册 MicroAppStateActions
实例用于通信,该实例有三个方法,分别是:
setGlobalState
:设置globalState
- 设置新的值时,内部将执行浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的观察者函数。
onGlobalStateChange
:注册观察者函数
- 响应
globalState
变化,在globalState
发生改变时触发该观察者函数。
offGlobalStateChange
:取消观察者函数
- 该实例不再响应
globalState
变化。
Actions
通信方案是通过全局状态池和观察者函数进行应用间通信,该通信方式适合大部分的场景。
2.3 应用路由
在这个时代,我们都期望 SPA 中的 URL 栏代表应用程序的视图状态,因此我们可以剪切、粘贴、邮件、文本和链接到它以直接跳转到应用程序内的页面。然而,在微前端应用程序中,应用程序状态实际上是状态的组合,每个微应用程序一个。我们如何表示和控制它?
解决方案是将每个微应用程序的状态编码到一个单一的复合 URL 中,并使用一个小型的宏应用程序路由器,它知道如何将复合 URL 放在一起并把它分开。不幸的是,这需要在每个微应用程序中使用特定于 Yumcha 的逻辑:从宏应用程序路由器接收消息并更新微应用程序的状态,相反地通知宏应用程序路由器该状态的变化,以便更新复合 URL。例如,可以想象一个YumchaLocationStrategy
用于 Angular 的元素,或者一个<YumchaRouter>
用于 React 的元素。
3、微前端的实现方式
3.1 服务器端模板组合
我们从一个明显的非创新的前端开发方法开始--在服务器上用多个模板或片段来渲染HTML。我们有一个index.html
,它包含了任何常见的页面元素,然后使用服务器端的 includes
来插入来自片段 HTML
文件的特定页面内容。
<html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>Feed me</title> </head> <body> <h1>🍽 Feed me</h1> <!--# include file="$PAGE.html" --> </body> </html>
我们使用 Nginx
为这个文件提供服务,通过与被请求的 URL
匹配,配置 $PAGE
变量。
server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; ssi on; # Redirect / to /browse rewrite ^/$ http://localhost:8080/browse redirect; # Decide which HTML fragment to insert based on the URL location /browse { set $PAGE 'browse'; } location /order { set $PAGE 'order'; } location /profile { set $PAGE 'profile' } # All locations should render through index.html error_page 404 /index.html; }
这是相当标准的服务器端组合。我们有理由称其为微前端的原因是,我们将代码分割成这样一种方式,即每一块都代表一个独立的领域概念,可以由一个独立的团队交付。这里没有显示的是这些不同的HTML文件是如何在网络服务器上结束的,但假设它们都有自己的部署管道,这使得我们可以在不影响或不考虑任何其他页面的情况下对一个页面进行部署修改。
为了获得更大的独立性,可以有一个单独的服务器来负责渲染和服务每个微型前端,由一个服务器在前面向其他服务器发出请求。通过对响应的仔细缓存,这可以在不影响延迟的情况下完成。
这个例子说明了微前端不一定是一种新技术,也不一定很复杂。只要我们注意我们的设计决定如何影响我们的代码库和团队的自主性,无论我们的技术栈如何,我们都可以实现许多相同的好处。
3.2 构建时集成
我们有时会看到的一种方法是将每个微前端作为一个包发布,并让容器应用程序将它们全部作为库依赖项包含在内。以下是容器如何package.json
查找我们的示例应用程序:
{ "name" : "@feed-me/container" , "version" : "1.0.0" , "description" : "A_food_delivery_web_app" , "dependencies" : { "@feed-me/browse-restaurants “:“^1.2.3”, “@feed-me/order-food”:“^4.5.6”, “@feed-me/user-profile”:“^7.8.9” } }
起初这似乎是有道理的。它像往常一样生成一个可部署的 Javascript 包
,使我们能够从各种应用程序中删除重复的公共依赖项。然而,这种方法意味着我们必须重新编译和发布每一个微前端,以便发布对产品任何单独部分的更改。就像微服务一样,我们已经看到这种 步调一致的发布过程所带来的痛苦,我们强烈建议不要使用这种微前端方法。
解决了将我们的应用程序划分为可以独立开发和测试的离散代码库的所有麻烦之后,让我们不要在发布阶段重新引入所有这些耦合。我们应该找到一种在运行时而不是在构建时集成我们的微前端的方法。
3.3 运行时集成
3.3.1 基于 iframe 的微前端
在浏览器中组合应用程序的最简单方法之一是简陋的 iframe
。就其本质而言,iframe
可以轻松地从独立的子页面构建页面。它们还在样式和全局变量方面提供了良好的隔离度,不会相互干扰。
使用
iframe
可以在父页面中加载子页面,并将子页面作为独立的应用运行。在微前端中,可以将不同的子应用封装成iframe
,并使用JavaScript
通信机制来实现应用之间的数据共享和交互。
<html> <head> <title>Feed me!</title> </head> <body> <h1>Welcome to Feed me!</h1> <iframe id="micro-frontend-container"></iframe> <script type="text/javascript"> const microFrontendsByRoute = { '/': 'https://browse.example.com/index.html', '/order-food': 'https://order.example.com/index.html', '/user-profile': 'https://profile.example.com/index.html', }; const iframe = document.getElementById('micro-frontend-container'); iframe.src = microFrontendsByRoute[window.location.pathname]; </script> </body> </html>
与服务器端包含选项一样 ,使用 iframe
构建页面并不是一项新技术,也许看起来也不那么令人兴奋。但是,如果我们重新审视 前面列出的 微前端的主要优势,iframe
最符合要求,只要我们注意我们如何分割应用程序和构建我们的团队。
我们经常看到很多人不愿意选择 iframe
。虽然有些不情愿似乎是出于直觉,认为 iframe
有点“讨厌”,但人们有一些很好的理由避免使用它们。上面提到的简单隔离确实会使它们不如其他选项灵活。在应用程序的不同部分之间构建集成可能很困难,因此它们使路由、历史记录和深层链接更加复杂,并且它们对使您的页面完全响应提出了一些额外的挑战。
优点:
- 安全性强:
iframe
可以将应用隔离开来,避免不同应用之间的代码和数据相互干扰。 - 可扩展性强:
iframe
可以将应用与服务器进行分离,使应用更容易进行扩展和升级。 - 兼容性好:
iframe
支持所有浏览器,没有兼容性问题。
缺点:
- 跨域通信问题:
iframe
中的应用与主页面之间需要通过JavaScript
通信机制来进行交互,但是这种跨域通信会涉及到一些安全问题,需要特别注意。 - 性能问题:
iframe
中的应用需要单独进行加载和渲染,可能会对应用的性能造成一定的影响。
推荐阅读:
3.3.2 基于模块化加载器的微前端
模块化加载器是一种前端工具,可以帮助管理 JavaScript 模块
的依赖关系和加载顺序。在微前端中,可以使用模块化加载器来加载和运行不同的子应用,并将它们组合成一个整体应用。
可能是最灵活的方法,也是最常采用的方法。每个微前端都使用标签包含在页面上
<script>
,并在加载时公开一个全局函数作为其入口点。然后容器应用程序确定应该挂载哪个微前端,并调用相关函数来告诉微前端何时何地渲染自己。
<html> <head> <title>Feed me!</title> </head> <body> <h1>Welcome to Feed me!</h1> <!-- 这些脚本不会立即渲染任何东西,而是将入口函数挂载到window上 --> <script src="https://browse.example.com/bundle.js"></script> <script src="https://order.example.com/bundle.js"></script> <script src="https://profile.example.com/bundle.js"></script> <div id="micro-frontend-root"></div> <script type="text/javascript"> // 这些全局函数通过上面的脚本附加到 window const microFrontendsByRoute = { '/': window.renderBrowseRestaurants, '/order-food': window.renderOrderFood, '/user-profile': window.renderUserProfile, }; const renderFunction = microFrontendsByRoute[window.location.pathname]; // 确定了入口函数后,我们现在调用它,为其提供应渲染自身的元素的 ID renderFunction('micro-frontend-root'); </script> </body> </html>
以上显然是一个原始的例子,但它展示了基本的技术。与构建时的集成不同,我们可以独立部署每个 bundle.js
文件。而且与 iframes
不同的是,我们可以完全灵活地在我们的微型前端之间建立集成,不管我们怎么想。我们可以以多种方式扩展上述代码,例如,只在需要时下载每个 JavaScript bundle
,或者在渲染一个微前端时将数据传入和传出。
这种方法的灵活性,再加上独立的部署性,使它成为我们的默认选择,也是我们最常看到的一种。
优点:
- 灵活性强:模块化加载器可以将不同的子应用进行模块化管理,使得应用更容易进行扩展和维护。
- 性能较好:模块化加载器可以根据需要进行动态加载和卸载,从而提高了应用的性能和加载速度。
缺点:
- 兼容性问题:模块化加载器需要浏览器支持
ES6 模块化
,否则需要使用polyfill
进行兼容处理。 - 需要进行配置:模块化加载器需要进行一些配置,可能会增加开发和部署的复杂度。