一文读懂微前端架构(微前端架构是什么)
ccwgpt 2024-09-20 13:13 40 浏览 0 评论
转载本文请注明出处:微信公众号EAWorld
前端开发在程序猿中无疑是一个比较苦逼的存在,作为一个前端开发,你必须要掌握Javascript,HTML,CSS这三大基础。Javascript作为网络时代最为重要的开发语言,由于其设计上的限制,一直在演进,经历了ES3,ES5,ES6(ECMAScript 2015)... ... 而简单的CSS也无法完成你复杂的需求,你需要Less/Sass/Sytlus来增强你的CSS的功能。这些还远远只是一小部分,你还需要了解:
- 你需要有网络的基本知识,和常见的API接口例如HTTP/REST/GraphQL
- 你需要知道浏览器的兼容性,什么IE,Chrome,Safari,Firefox,等等
- 你需要知道应用如何打包,了解 Webpack ,还有gulp, Babel, Parcel, Browserify, and Grunt等是怎么工作的
- 你需要熟悉HTML的DOM操作,和相关的工具库例如jquery和d3js
- 需要了解不同的框架和他们的优缺点 例如React,AngularJS和VueJS,这三大框架都不兼容,而且各自都有自己庞大的生态
- 虽然NodeJS主要用于后端开发,但是很多前端的工具链和这个相关,例如构建工具 npm,yarn ...
- 如果你的项目足够复杂,你需要引入Monorepo,使用例如lerna,nx等工具来管理多个项目的包和依赖
- 你需要掌握基本的前端测试工具和框架,例如:Jasmin,Jest,Selenium,Mocha等等
最可怕的是,这些东西都在飞快地发展和变化中,当你正忙于学习ES8,ES9,ES10的新特性的时候,今天我要和大家分享的希望不是压死前端开发小骆驼的最后一根稻草--微前端。
目录:
1.什么是微前端
2.为什么需要微前端,它有什么优势
3.如何实现微前端架构
4.运行时微前端的具体实现方式
5.微前端的问题和缺点
一、什么是微前端
而提到微前端就离不开微服务,大家对微服务都比较熟悉了,微服务允许后端体系结构通过松散耦合的代码库进行扩展,每个代码库负责自己的业务逻辑,并公开一个API,每个API均可独立部署,并且各自由不同的团队拥有和维护。
前端架构经历了从单体,到前后端分离,再到微服务,最终发展到现在的微前端的过程如下图所示:
微前端的思路是把微服务的架构引入到前端,其核心都是要能够以业务为单元构建端到端的垂直架构,使得单个的团队能够独立自主的进行相关的开发,同时又具备相当的灵活性,按需求来组成交付应用。
“微前端”一词最早于2016年底在ThoughtWorks 技术雷达中提出的。它将微服务的概念扩展到了前端世界。当前的趋势是构建一个功能强大且功能强大的浏览器应用程序(也称为单页应用程序),该应用程序位于微服务架构之上。随着时间的流逝,通常由一个单独的团队开发的前端层会不断增长,并且变得更加难以维护。
微前端背后的想法是将网站或Web应用程序视为由独立团队拥有的功能的组合。每个团队都有自己关心和专长的不同业务或任务领域。一个团队是跨职能的,并且从数据库到用户界面,端到端地开发其功能。
但是,这个想法并不新鲜。它与“单体系统”概念有很多共同点。在过去,类似的方法被称为“垂直系统的前端集成”。但是微前端显然是一个更友好,更轻巧的术语。
在微服务的架构中,后台的服务已经按照业务进行了分离,而前端仍然是一个单体构建,通过网关来调用不同的后台服务。这个微服务的思路是相违背的,这也就造成了你的后端团队是按照业务分割的,但是前端团队仍然是一个整体。微前端可以有效地改进这一点。
微前端的核心思路其实是远程应用程序,包含组件/模块/包的运行时加载。
如上图,对于用户而言,访问的是一个微前端的容器(container),容器加载运行在远程服务上的应用,把这些远程应用作为组件/模块/包在本地浏览器中加载。
- 组件是底层UI库的构建单元
- 模块是相应运行时的构建单元
- 包是依赖性解析器的构建单元
- 微前端是所提出的应用程序的构建块
二、为什么需要微前端?
它有什么优势?
在前面我们看到的微前端之前的架构,所有的前端还是一个单体,前端团队会依赖所有的服务或者后台的API,前端开发会成为整个系统的瓶颈。使用微前端,就是要让前端业务从水平分层变为垂直应用的一部分,进入业务团队,剥离耦合。
那么微前端有什么好处,为什么要采用微前端架构呢?
- 各个团队独立开发,相互不影响,独立开发、独立部署,微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 增量升级,在面对各种复杂场景时,通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略。因为是运行时加载,可以在没有重建的情况下添加,删除或替换前端的各个部分。
- 不受技术影响,每个团队都应该能够选择和升级其技术栈,而无需与其他团队进行协调。也就是说A应用可以用React,而B应用使用Vue,大家可以通过同一个微前端来加载
- 独立运行时,每个微应用之间状态隔离,运行时状态不共享。隔离团队代码,即使所有团队都使用相同的框架,也不要共享运行时。构建自包含的独立应用程序。不要依赖共享状态或全局变量。
- 建立团队命名空间,对于CSS,事件,本地存储和Cookies,可以避免冲突并阐明所有权。
因此,微前端和微服务的本质都是关于去耦合。而只有当应用程序达到一定规模时,这才开始变得更有意义。
三、如何实现微前端架构
微前端不是一个库,是一种前端架构的设计思路,要实现微前端,本质上就是在运行时远程加载应用。
实现微前端,有几个思路,从构建的角度来看有两种,编译时构建微前端和运行时构建微前端:
- 编译时微前端,通常将第三方库中的组件作为包,在构建时引入依赖。这种实现引入新的微前端需要重新编译,不够灵活。编译时的微前端可以通过Web Components,Monorepo等方式来实现。其中Monorepo非常流行,常见的工具有nx,rush,lerna等。
- 运行时微前端,是一次加载或通过延迟加载按需动态将微型前端注入到容器应用程序中时。当引入新的微前端的时候,不需要构建,可以动态在代码中定义加载。我眼中的微前端更多是指这种运行时加载的微前端,因为独立构建,部署和测试是我们对于“微”的定义。
从前后端责任分层来看,可以从前端或者后端来实现。
通过客户端框架来实现
微前端通常由客户端工具来支持实现(听上去好有道理),有许多支持客户端开发微前端的实现工具,包括:Piral,Open Components,qiankun,Luigi,Frint.js等。其中qiankun是蚂蚁金服开发的。
在客户端还可以通过辅助库的方式来实现,辅助库可以为共享依赖项,路由事件或不同的微前端及其生命周期来提供一些基础架构。
下面的一个示例是通过诸如导入映射或打包特定块等机制处理共享依赖关系。
相关的工具有Webpack5 Module Federation,Siteless,Single SPA,Postal.js等
通过服务器端实现
微前端并非只能在客户端来实现,类似于服务端渲染,同样可以通过服务端来实现。
服务端微前端的支持工具有:Mosaic,PuzzleJs,Podium,Micromono等。
好了,说了这么多我相信你是一脸懵逼的,到底怎么实现的?我们抛开架构不说,来看看到底如何实现吧。
四、运行时微前端的具体实现方式
Iframe
iframes是可以在html中嵌入另一个HTML。下面就是用iframe实现微前端的一个例子:
<!DOCTYPE html>
<html>
<body>
<iframe src="http://localhost:3006" width="600" height="900">
<p>Your browser does not support iframes.</p>
</iframe>
<iframe src="http://localhost:3007" width="600" height="900">
<p>Your browser does not support iframes.</p>
</iframe>
</body>
</html>
如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了。iframe 提供了浏览器原生的隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但它的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。这里的主要问题包括:
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- UI 不同步,DOM 结构不共享。
- 全局上下文完全隔离,内存变量不共享。
- 慢。每次子应用进入都需要次浏览器上下文的重建、资源重新加载。
所以虽然使用iframe可以实现远程加载的效果,但是因为这些限制,很少会有应用会使用。
Nginx路由
利用Ngix路由,我们可以把不同的请求路由到不同的微前端的应用。
例如Nginx的路由能力,在前端可以动态请求不同的后端应用,而每一个后端应用独立运行,前端可以把这些不同的后端应用加载,编排在一起。下面的代码是一个Nginx的配置,customers/users/admins分别表示了三个不同的应用,前端通过路由来加载位于不同服务的后端应用。
worker_processes 4;
events { worker_connections 1024; }
http {
server {
listen 80;
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
location /app1 {
try_files $uri app1/index.html;
}
location /app2 {
try_files $uri app2/index.html;
}
location /app3 {
try_files $uri app3/index.html;
}
}
}
无论你采用哪一种的微前端架构,Nginx方向代理或者其它的API网关的解决方案都能够提供方便灵活的后端路由功能。但是通过这种方式,需要定义一个通用可扩展的路由规则,否则当引入新的应用的时候,还需要修改Nginx的路由配置,那就很不方便了。
Webpack 5 Module Federation
Webpack5 的Module Federation是一个令人兴奋的革新,它能够很方便的支持微前端的构建。模块联合允许JavaScript应用程序从另一个应用程序动态加载代码,并在此过程中能共享依赖关系。如果使用Module Federation的应用程序不具有联合代码所需的依赖关系,则Webpack将从该联合构建源中下载缺少的依赖关系。
在Module Federation的上下文中,启动代码是一种将运行时代码附加到远程容器启动序列的实施策略。这真的很有用,因为通过Hook无法访问ModuleFederation及其运行时,无法对其进行扩展或添加一行代码,这些代码可以像动态设置远程容器的公共路径那样进行操作。这在普通的webpack应用程序中是微不足道的,但是在一个无法访问的自定义运行时容器中却很难做到,该容器为模块联合远程编排提供了动力。简单来说,Module Federation注入一段运行时的代码来负责加载和编排远程的应用代码,并能够管理和加载远程应用的依赖。
下面是一个对应的例子:
module.exports = {
mode: 'development',
devServer: {
port: 8080,
},
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
microFrontEnd1: 'microFrontEnd1@http://localhost:8081/remoteEntry.js',
microFrontEnd2: 'microFrontEnd2@http://localhost:8082/remoteEntry.js', },
})
]
};
上面的代码是微前端的容器端的配置,容器负责加载其它远程应用的代码。这个例子里,它加载了两个远程应用。
module.exports = {
mode: 'development',
devServer: {
port: 8081,
},
plugins: [
new ModuleFederationPlugin({
name: 'microFrontEnd1',
filename: 'remoteEntry.js',
exposes: {
'./MicroFrontEnd1Index': './src/index',
},
}),
]
};
每一个微前端的Webpack配置如上。
利用ModuleFederationPlugin,remote可以用来加载远端的应用,而Expose可以把自己的组件暴露为远端组件。
在container中,只需要调用以下的代码来加载远端组件。
import 'microFrontEnd1/MicroFrontEnd1Index';
import 'microFrontEnd2/MicroFrontEnd2Index';
Module Federation的加载过程如上图所示:
- localhost 加载index.html
- main.js 是Module Federation的核心的编排代码,负责加载远程组件。
- remoteEntry.js 是Module Federation暴露的远程组件的代码
- src_ 是打包后的代码,其中 bootstrap_js是容器侧的代码,index_js是微前端侧的代码。
Module Federation实现了类似动态链接库的能力,可以在运行时加载远程代码,远程代码本质上是一个加载在window上的全局变量,Module Federation可以帮助解决依赖的问题。Javascrip作为上古语言,没有提供依赖管理,导致留给各路大神各种发挥的空间。
Module Federation的缺点就是依赖Webpack 5,包直接挂载为全局变量。
EMP微前端是基于Module Federation的微前端解决方案。
Single SPA
单页面应用是当今为Web应用的主流,区别于传统的多页面应用,整个SPA只有一个页面,其内容都是通过Javascript的功能来加载。
SPA是一个Web应用程序,仅包含一个HTML页面。提供动态更新,它允许在不刷新页面的情况下与页面进行交互。利用单页应用程序,可以显着降低服务器负载并提高加载速度,从而获得更好的用户体验,因为SPA仅在先前加载整个页面时才按需导入数据。
除了开发复杂,对于SEO不友好,但页面应用的最大技术缺陷是URL不适合共享,因为SPA只有一个地址。
single-spa是一个框架,用于将前端应用程序中的多个JavaScript微前端组合在一起。
使用single-spa构建前端可以带来很多好处,例如:
- 在同一页面上使用多个框架而无需刷新页面(React,AngularJS,Angular,Embe)
- 独立部署微前端
- 使用新框架编写代码,而无需重写现有应用程序
- 延迟加载代码可缩短初始加载时间
single-spa应用程序包含以下内容:
- single-spa根配置,用于呈现HTML页面和注册应用程序的JavaScript。每个应用程序都注册了以下三项内容:name,加载应用程序代码的函数,确定应用程序何时处于活动状态/非活动状态的函数,
- 打包成模块的单页应用程序的应用程序。每个应用程序必须知道如何从DOM引导,安装和卸载自身。传统SPA和Single-SPA应用程序之间的主要区别在于,它们必须能够与其他应用程序共存,因为它们各自没有自己的HTML页面。例如,React或Angular SPA应用程序。处于活动状态时,他们可以侦听url路由事件并将内容放在DOM上。处于不活动状态时,它们不侦听url路由事件,并且已从DOM中完全删除。
Single-SPA注册的应用程序拥有普通SPA所具有的所有功能,只是它没有HTML页面。SPA包含许多已注册的应用程序,每个应用程序都有其自己的框架。已注册的应用程序具有其自己的客户端路由和它们自己的框架/库。它们呈现自己的HTML,并且在安装时有完全的自由去做他们想做的任何事情。挂载的概念是指已注册的应用程序是否正在将内容放在DOM上。决定是否挂载已注册应用程序的是其活动功能。每当未挂载已注册的应用程序时,它都应保持完全休眠状态直到挂载。
Single SPA的样例代码如下:
1. 微前端代码:
import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./root.component";
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Root,
errorBoundary(err, info, props) {
// Customize the root error boundary for your microfrontend here.
return null;
},
});
export const { bootstrap, mount, unmount } = lifecycles;
Single SPA的微前端是纯的JS组件,不包含HTML,需要通过容器来加载。
2.容器的Root Config
在容器侧,需要通过Import Map或者Webpack来定义远程组件,并注册远程应用。
{
"imports": {
"@naughty/root-config": "//localhost:9000/naughty-root-config.js",
"@naughty/app": "//localhost:8500/naughty-app.js",
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"
}
}
容器侧的HTML文件使用import map来定义远程依赖,其中root-config是编排代码,负责远程应用的注册和加载。同时需要定义所有共享的依赖,这里例子中是react和react-dom
import { registerApplication, start } from "single-spa";
registerApplication({
name: "@single-spa/welcome",
app: () =>
System.import(
"https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
),
activeWhen: ["/welcome"],
});
registerApplication(
'@naughty/app',
() => System.import('@naughty/app'),
location => location.pathname.startsWith('/app')
);
start({
urlRerouteOnly: true,
});
在root-config中,我们注册了两个远程应用,使用不同的url来加载。/welcome会加载welcome应用,而/app会加载我们的app应用。
Single SPA的核心是利用不同的URL路由来加载远程组件,它可以和Webpack(打包时构建依赖)或者Import Map(运行时使用浏览器导入依赖)一起工作。注意,不要在你的微前端中混用两种依赖机制。
Single SPA还提供一个layout 引擎,可以帮助你快速的构建微前端。
相比Module Federation,Single SPA的代码和生命周期的管理更清楚,提供清晰的接口,缺点是共享的依赖需要手工通过import map来管理。
要做一个好的微前端因为受限于浏览器和JS的一些特性,并不容易。除了我们今天分享的内容,还面临着诸多的挑战:如何解决css/js的冲突,使得组件和应用完全隔离;如何解决不同应用间的通信;如何处理路由;如何保证UI风格的统一等等。
五、微前端的问题和缺点
讲了这么多的优点和实现,那么微前端是不是解决前端开发问题的银弹呢?当然不是。所有的架构都是取舍和权衡,这个世界上并不存在银弹,微前端架构和微服务一样也存在他的弊端,单体架构未必就差。
1. 微前端的构建通常比较复杂,从工具,打包,到部署,微前端都是更为复杂的存在,天下没有免费的午餐,对于小型项目,它的成本太高。
2. 每个团队可以使用不同的框架,这个听上去很美,但是实际操作起来,除了要支持历史遗留的应用,它的意义不大。同时也为带来体验上的问题。可以远程加载不同的框架代码是一回事,把它们都用好是另一回事。
3. 性能上来看,如果优化得不好微前端的性能可能会存在问题,至少微前端框架是额外的一层加载。如果不同的微前端使用了不同的框架,那么每一个框架都需要额外的加载。
微前端架构还在发展之中,本文提到的iframe/nginx/module federation/single-spa只是诸多解决方案中的一小部分,前端的发展变化和生态系统实在是丰富,其他的方案诸如umd/乾坤,Piral,open comonent等等。当使用你也可以选择标准的Web Component或者ES Modules来构建微前端,但是这些标准的浏览器支持不是特别好,这个是前端开发永远的痛。(诅咒IE)
大家对于微前端有什么想法或者问题,欢迎一起讨论。
关于作者:陶刚,Splunk资深软件工程师,架构师,毕业于北京邮电大学,现在在温哥华负责Splunk机器学习云平台的开发,曾经就职于SAP,EMC,Lucent等企业,拥有丰富的企业应用软件开发经验,熟悉软件开发的各种技术,平台和开发过程,在商务智能,机器学习,数据可视化,数据采集,网络管理等领域都有涉及。
关于EAWorld:使能数字转型,共创数智未来!长按二维码关注!
相关推荐
- 十分钟让你学会LNMP架构负载均衡(impala负载均衡)
-
业务架构、应用架构、数据架构和技术架构一、几个基本概念1、pv值pv值(pageviews):页面的浏览量概念:一个网站的所有页面,在一天内,被浏览的总次数。(大型网站通常是上千万的级别)2、u...
- AGV仓储机器人调度系统架构(agv物流机器人)
-
系统架构层次划分采用分层模块化设计,分为以下五层:1.1用户接口层功能:提供人机交互界面(Web/桌面端),支持任务下发、实时监控、数据可视化和报警管理。模块:任务管理面板:接收订单(如拣货、...
- 远程热部署在美团的落地实践(远程热点是什么意思)
-
Sonic是美团内部研发设计的一款用于热部署的IDEA插件,本文其实现原理及落地的一些技术细节。在阅读本文之前,建议大家先熟悉一下Spring源码、SpringMVC源码、SpringBoot...
- springboot搭建xxl-job(分布式任务调度系统)
-
一、部署xxl-job服务端下载xxl-job源码:https://gitee.com/xuxueli0323/xxl-job二、导入项目、创建xxl_job数据库、修改配置文件为自己的数据库三、启动...
- 大模型:使用vLLM和Ray分布式部署推理应用
-
一、vLLM:面向大模型的高效推理框架1.核心特点专为推理优化:专注于大模型(如GPT-3、LLaMA)的高吞吐量、低延迟推理。关键技术:PagedAttention:类似操作系统内存分页管理,将K...
- 国产开源之光【分布式工作流调度系统】:DolphinScheduler
-
DolphinScheduler是一个开源的分布式工作流调度系统,旨在帮助用户以可靠、高效和可扩展的方式管理和调度大规模的数据处理工作流。它支持以图形化方式定义和管理工作流,提供了丰富的调度功能和监控...
- 简单可靠高效的分布式任务队列系统
-
#记录我的2024#大家好,又见面了,我是GitHub精选君!背景介绍在系统访问量逐渐增大,高并发、分布式系统成为了企业技术架构升级的必由之路。在这样的背景下,异步任务队列扮演着至关重要的角色,...
- 虚拟服务器之间如何分布式运行?(虚拟服务器部署)
-
在云计算和虚拟化技术快速发展的今天,传统“单机单任务”的服务器架构早已难以满足现代业务对高并发、高可用、弹性伸缩和容错容灾的严苛要求。分布式系统应运而生,并成为支撑各类互联网平台、企业信息系统和A...
- 一文掌握 XXL-Job 的 6 大核心组件
-
XXL-Job是一个分布式任务调度平台,其核心组件主要包括以下部分,各组件相互协作实现高效的任务调度与管理:1.调度注册中心(RegistryCenter)作用:负责管理调度器(Schedule...
- 京东大佬问我,SpringBoot中如何做延迟队列?单机与分布式如何做?
-
京东大佬问我,SpringBoot中如何做延迟队列?单机如何做?分布式如何做呢?并给出案例与代码分析。嗯,用户问的是在SpringBoot中如何实现延迟队列,单机和分布式环境下分别怎么做。这个问题其实...
- 企业级项目组件选型(一)分布式任务调度平台
-
官网地址:https://www.xuxueli.com/xxl-job/能力介绍架构图安全性为提升系统安全性,调度中心和执行器进行安全性校验,双方AccessToken匹配才允许通讯;调度中心和执...
- python多进程的分布式任务调度应用场景及示例
-
多进程的分布式任务调度可以应用于以下场景:分布式爬虫:importmultiprocessingimportrequestsdefcrawl(url):response=re...
- SpringBoot整合ElasticJob实现分布式任务调度
-
介绍ElasticJob是面向互联网生态和海量任务的分布式调度解决方案,由两个相互独立的子项目ElasticJob-Lite和ElasticJob-Cloud组成。它通过弹性调度、资源管控、...
- 分布式可视化 DAG 任务调度系统 Taier 的整体流程分析
-
Taier作为袋鼠云的开源项目之一,是一个分布式可视化的DAG任务调度系统。旨在降低ETL开发成本,提高大数据平台稳定性,让大数据开发人员可以在Taier直接进行业务逻辑的开发,而不用关...
- SpringBoot任务调度:@Scheduled与TaskExecutor全面解析
-
一、任务调度基础概念1.1什么是任务调度任务调度是指按照预定的时间计划或特定条件自动执行任务的过程。在现代应用开发中,任务调度扮演着至关重要的角色,它使得开发者能够自动化处理周期性任务、定时任务和异...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- grpc框架 (55)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)