图片
为了防止概念有点抽象,可以看一个具体的例子:上图是一个微前端的demo,主应用中有导航栏,footer组件以及左边的侧边栏组件,而右面是子应用部分,这里的子应用并没有集成在主应用中,只是通过微前端的框架内嵌到主应用中,可是给用户的感受就是一个完整的项目。
(二)特点
目前的微前端框架一般都具有以下 三个 特点:
- 技术栈无关:主框架不限制接入应用的技术栈,子应用具备完全自主权。
- 独立性强:独立开发、独立部署,子应用仓库独立。
- 状态隔离:运行时每个子应用之间状态隔离。
(三)为什么出现微前端
2014年:Martin Fowler和James Lewis共同提出了 微服务 的概念。微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的API进行通信的 小型独立服务 组成。
微服务的主要思路是:
- 将应用 分解 为小的、互相连接的微服务,一个微服务完成某个 特定功能 。
- 每个微服务都有自己的业务逻辑和适配器,不同的微服务,可以使用 不同的技术 去实现。
- 使用 统一的网关 进行调用。
可以看到微服务的主要思路是化繁为简,通过更加细致的划分,使得服务内部更加内聚,服务之间耦合性降低,有利于项目的团队开发和后期维护。把微服务的概念应用到前端, 前端微服务/微前端服务 就诞生了,简称其为微前端。
2018年: 第一个微前端工具single-spa在github上开源。
2019年: 基于single-spa的qiankun问世。
2020年:Module Federation(webpack5)把项目中模块分为本地模块和远程模块,远程模块不属于当前构建,在运行时从所谓的容器加载。加载远程模块是异步操作。当使用远程模块时,这些异步操作将被放置在远程模块和入口之间的下一个chunk的加载操作中,从而实现微前端的构建。
二、微前端的实现方式
(一)服务端集成
微前端的第一种实现思路是服务端集成,即通过 Nginx 配置反向代理来实现不同路径映射到不同应用(如下图所示),这样可以实现项目的独立开发和部署。
图片
但同时这种做法也会丢失SPA的体验,每一次命中路由都会重新请求资源,不能局部更新当前页面。
(二)运行时集成
另一种方法就是运行时集成,这种方法一种实现就是使用 iframe ,通过配置不同的src加载不同的子应用页面。
<iframe src="https://test.qq.com/a/index.html"></iframe>
iframe 优点 :
- iframe 自带的样式、环境隔离机制使得它具备天然的 沙盒机制 。
- 嵌入 子应用比较 简单 。
iframe 缺点 :
- iframe功能之间的跳转是无效的,刷新页面 无法保存 状态。
- URL的记录完全无效,刷新会返回首页。
- 主应用 劫持 快捷键操作,事件冒泡 不穿透 到主文档树上。
- 模态弹窗的背景是无法覆盖到整个应用。
- iframe应用加载失败,内容发生错误主应用无法感知, 通信麻烦 。
综上,iframe也可以实现微前端,但是需要解决其自身的诸多弊端。公司的 无界微前端 就是基于iframe实现的。
三、现有开源方案
(一)single-spa
single-spa是一个用于 前端微服务化 的JavaScript前端解决方案。single-spa的核心就是定义了一套 协议 。协议包含主应用的配置信息和子应用的生命周期,通过这套协议,主应用可以方便的知道在什么情况下激活哪个子应用。
- 配置信息
在single-spa中的配置信息也称为:Root Config,如下就是具体的配置项。需要配置子应用的名称,加载方式以及加载时机。
{
name: "subApp1", //子应用的名称
app: () =>//告诉主应用如何加载子应用的代码,
System.import("/a/b/subAPP/code"),
activeWhen: "/subApp1", //告诉主应用何时激活子应用
}
single-spa提供registerApplication将子应用的信息注册到主应用中。
singleSpa.registerApplication(
{
name: 'appName',
app: () => System.import('appName'),
activeWhen: '/appName',
})
在上面的代码中System.import让人比较在意,这是什么呢 ?
这个问题要从主应用如何加载子应用说起,在single-spa中子应用要实现生命周期函数,然后导出给主应用使用。关键就是这个“导出”的实现,这涉及到 JavaScript 的模块化问题,即需要把子应用打包成一个包含生命周期的模块,让主应用引入。
JavaScript的模块化,如何在页面中引入模块 ?
JavaScript的模块化就是将JavaScript程序拆分为可按需导入的单独模块的机制。Node.js已经提供这个能力很长时间了,还有很多的Javascript库和框架已经开始了模块的使用(例如CommonJS和基于AMD的其他模块系统 如RequireJS,以及最新的Webpack和Babel)。目前最新的浏览器也开始原生支持模块功能。
- 在