天下大势,分久必合合久必分。大型组织的组织结构、软件架构在不断的发生变化。一款软件从最初的单一,进行不断的细化,最终变得庞大,从而不得不拆分到不同部门,出现多样化。然而在多样化的道路上,用户厌倦了一家公司的软件却要分散于不同的应用中,于是应用又再一次走向聚合。
***
一、微前端
1、什么是微前端
微前端类似于微服务,它将微服务的理念应用于浏览器端。
(1)微服务:把后端的功能进行了拆分,使用统一的网关进行调用。方便独立部署和维护升级, 不同的微服务,可以使用不同的技术去实现
(2)微前端:有统一的加载器,各个子应用,也可以使用不同的框架。但是同一团队中,技术栈相同有利于管理和协作,方便子应用整合。
***
对前端应用进行拆分,将不同的功能按照不同的维度拆分成多个子应用,实现应用的自治。微前端的核心在于拆, 拆完后再合!
特点如下: (1)独立开发、独立部署 子应用可以分别让多个团队开发,部署的时候只需要更新我们希望更新的应用,整体不受影响 (2)单一职责 重点关注本应用的功能,降低耦合 (3)技术栈无关 每个团队可以自主选择技术栈,再接入主应用
2、为什么使用微前端
(1)老代码迁移 之前的老应用,已经稳定运行了,并且没有新功能,没有理由去重写这一整套,这个时候就可以使用微前端,直接整合到新应用中 (2)前端聚合 现在有很多这种情况,一家公司会提供一系列的产品,而用户并不想用这么多应用,在用户眼里,可能觉得明明是一家公司,用一个产品就够了,因此聚合也就成了一个趋势 (3)流行 因为流行去学习一项新技术,并没有什么问题,新技术意味着可以在一定程度上提升技术水平和技术影响力。但是,如果对于这样一个“流行”的技术,没有多加研究,就直接在项目中使用,难免会出现一些问题
3、微前端架构模式
(1)基座模式
通过一个主应用来管理所有子应用,他可以只是单纯的基座功能,也可以带有业务功能,一般业务都是核心部分业务,如用户登录、菜单管理等。只需要设计好对应应用加载机制,何时加载,何时卸载。
优点:难度小、方便实践 缺点:通用度低 例如:用户想访问A应用必须先加载主应用,然后才能加载A应用
(2)自组织模式
应用各管各,相互独立,各自拥有一个小型的基座管理功能,每个应用都可以是基座,从而每个应用有了更高的自主性,但是这样会导致出现很多重复代码。
优点:通用度高 缺点:设计难度大 例如:用户想要访问A应用,不需要加载其他应用,直接可以打开
4、微前端拆分方式
不合理的采用微前端,可能会带来很多问题,如前端基础设施不完善,会导致各个应用有大量的重复代码。另外一个简单的业务,是否真的有必要成为一个单独的应用,将一个整体拆分成了很多个小应用,是否真的能提高效率,还是变得更加不便维护了呢。面对这些问题,我们要采取合适的拆分策略。
(1)按照业务拆分 在一个系统中,如果同时存在多个系统,如订单系统、库存系统、物流系统等,每个系统有自己的业务,关联也不是很紧密,那么拆分起来就很轻松了。 但是如果业务本身耦合严重,比如一个操作人员,需要同时操作这三个系统,那么拆分起来就不是很容易了。 因此,对于由业务性质决定的应用,只能根据业务是否隔离来进行拆分。 (2)按照权限拆分 比如一个网站有前后台两个系统,前台系统只能用户使用,后台系统只能管理员使用,那么就可以拆分开来。 (3)按照变更的频率拆分 在一个应用中,并不是所有部分都在不断迭代,有些功能可能上线后因为用户量少,几乎不修改。若是将应用中频繁变更的部分拆分出来,不仅更容易维护,还可以减少频繁的更改给其他部分带来的问题。 (4)按照组织结构拆分 这种更适合同一个应用,几个团队一起开发,按照团队来划分不同的前端应用。但是如果一个团队维护着多个业务模块,随着业务的增多和变复杂,还是需要进一步拆分的。 (5)跟随后端微服务拆分 一旦后端拥有相关服务,前端也可跟随拆分,但是后端拆分方式并不全部适用于前端,还是要具体问题具体分析。
二、微前端实现方式
1、服务端集成
通过Nginx配置反向代理来实现不同路径映射到不同应用
代码语言:txt复制 server {
listen 80; #监听80的服务端口
server_name localhost; #监听的域名
location /{
root /base;
}
location /vue-app {
proxy_pass http://127.0.0.1:3000/micro-app/vue-app;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' '*';
}
location /react-app {
proxy_pass http://127.0.0.1:4000/micro-app/react-app;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' '*';
}
}
优点:成本比较低,可以实现独立开发和部署 缺点:丢掉一部分的 SPA 体验,每次跳转都会刷新整个页面
2、构建时集成
在构建时将拆分开的子应用打包成一个应用,使用 npm 包的方式加入到主应用
代码语言:txt复制{
"dependencies": {
"@vue-app": "^0.0.1",
"@react-app": "^0.0.1",
}
}
优点:独立开发,保留了 SPA 体验 缺点:无法独立部署,且部署的成本非常高,一个子应用更新就需要重新构建整个应用
3、运行时集成
使用 iframe,通过改变 iframe 的 src 属性来加载对应的子应用
代码语言:txt复制<iframe src="https://vue-app/index.html"></iframe>
优点:简单易上手,接入成本低;自带沙箱机制 缺点:url 不同步,浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用;UI 不同步,DOM 结构不共享,一个iframe 中的元素只能在当前 iframe 中展示;全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等不方便;慢,每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程
三、微前端开源方案
Single-SPA:用于前端微服务化的JavaScript前端解决方案 (本身没有处理样式隔离、js执行隔离) 实现了路由劫持和应用加载;但是加载文件需要自己构建script标签,但是加载文件需要自己构建script标签,主应用必须得手动加载子应用打包好的lib库文件,如果子应用比较多,比较麻烦,不灵活;样式不隔离,父子应用会相互影响,没有js沙箱机制,共用同一个window。
qiankun:基于Single-SPA, 提供了更加开箱即用的 API (single-spa sandbox import-html-entry),它做到了技术栈无关,并且接入简单。主框架不限制接入应用的技术栈,微应用具备完全自主权,微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新,每个微应用之间状态隔离,运行时状态不共享。
以上两种是现阶段比较常用且较成熟的方案,后期文章中会详细介绍,除此之外,还有Piral、Luigi,以及腾讯的微前端oteam和无界(基于iframe)以及Hel微前端,美团的Bifrost,字节的lModern.js和lGarfish,感兴趣的可以了解一下。
四、最后
实施微前端并不容易,我们要深入框架原理,了解各种相关的实现,才能封装出适合自己的方案。尽管都有一些缺陷,不一定是最好的实践,但随着技术的发展,一定会出现更好的方式,来实现目标。