micro-iframe
Version:
a communication library for iframe-based micro-frontend integration
215 lines (191 loc) • 9.31 kB
Markdown
# 🚀 micro-iframe
> 🔥 一个基于 iframe + postMessage 实现微前端通信交互的三方库
### 一、项目背景与设计目标
___
- 背景:随着前端业务复杂度提升,微前端架构成为大型项目的主流方案。你肯定也和我一样使用过乾坤、micro-app、wujie这些成熟的微前端三方库,但经过一段时间的使用我发现这些三方库并不是完全理想的方案,你会发现对于跨域、资源地址、三方js加载等问题总是伴随微前端三方库,并且有时很难解决。特别是当你项目中有使用Cesium这类本身就带有静态资源、Worker嵌入微前端应用后,多多少少会有问题,且很难解决。我发现iframe 作为最原生、隔离性最强的微前端实现方式,天然支持多应用独立运行、技术栈无关、样式与路由隔离是非常适合做微前端的方案,如果再打通iframe间的消息通信,那微前端的基本功能即可完成。
- 目标:提供一个简单易用、灵活可扩展的 iframe 微前端集成工具,降低主应用与子应用集成门槛,提升开发效率。
- 声明:本库专注于 iframe 微前端场景,适合需要多系统集成、渐进式迁移、独立部署的前端项目。
### 二、项目原理
___
- micro-iframe会维护一个路由表,用于记录你的应用的结构关系信息,包括子应用的名称、id、origin等
- iframe的通信是通过postMessage实现,几乎浏览器都支持,且可以跨域
- iframe和iframe通过消息进行通信,路由表会记录应用到应用间传递的路径,micro-iframe会根据路径层层转发消息,直到消息传递到目标应用
### 三、安装与快速上手
___
#### 安装
```bash
npm i micro-iframe
```
#### 主应用中
```javascript
import { MicroMainApp } from 'micro-iframe'
// 在主应用中注册路由表
// 第一个参数是主应用的名称,第二个参数是路由表(路由表是一个具有层级关系的树形结构)
window.microIframe = new MicroMainApp('MAIN_APP', {
name: 'MAIN_APP',
iframeId: 'main-app',
origin: 'http://localhost:3000',
children: [
{
name: 'CHILD_APP1',
iframeId: 'child-app1',
origin: 'http://localhost:3001',
children: [
{
name: 'CHILD_APP3',
iframeId: 'child-app3',
origin: 'http://localhost:3003'
}
]
},
{
name: 'CHILD_APP2',
iframeId: 'child-app2',
origin: 'http://localhost:3002',
children: [
{
name: 'CHILD_APP4',
iframeId: 'child-app4',
origin: 'http://localhost:3004'
}
]
},
]
})
// 监听外部传递的消息 - 写法与mitt库类似
// 第一个参数是监听事件名称,第二个参数是回调函数
window.microIframe.$on('MainAppType', (message) => {
console.log(message)
})
// 发送消息给到子应用 - 写法与mitt库类似
// 第一个参数是发送目标应用名称,第二个参数是发送事件名称,第三个参数是发送消息内容
window.microIframe.$emit('CHILD_APP1', 'ChildApp1Type', '主应用 -> 子应用1的消息')
```
#### 子应用对接
子应用无需特殊改造,建议在 window 上监听 message 事件实现与主应用通信。
```javascript
import { MicroChildApp } from 'micro-iframe'
// 注册子应用,第一个参数是主应用的名称
window.microIframe = new MicroChildApp('CHILD_APP1')
// 监听外部传递的消息 - 写法与mitt库类似
// 第一个参数是监听事件名称,第二个参数是回调函数
window.microIframe.$on('ChildApp1Type', (message) => {
console.log(message)
})
// 发送消息给到其它应用 - 写法与mitt库类似
// 第一个参数是发送目标应用名称,第二个参数是发送事件名称,第三个参数是发送消息内容
window.microIframe.$emit('MAIN_APP', 'MainAppType', '子应用1 -> 主应用的消息')
```
### 四、API 说明
___
#### `new MicroMainApp(microName: string, microAppRoute: MicroIFrameRouteNode)`
- 用途: 创建主应用实例,使用主应用中
- `microName`: 主应用名称,和路由表中名称对应
- `microAppRoute`: 应用路由表,是由主应用为根节点的一棵树形结构,树形结构能正确反应应用间的层级关系。其中每个节点的属性如下:
- `name`: 应用名称
- `iframeId`: iframe 元素的 id,用于查找iframe,请一定给iframe设置id属性
- `origin`: iframe 元素的 origin,即你项目部署的域名端口,这是postMessage通信的必要信息
- `children`: 该应用的下属子应用
#### `new MicroChildApp(microName: string)`
- 用途: 创建子应用实例,使用所有子应用中
- `microName`: 子应用名称,和路由表中名称对应
#### `$on(msgType: string, msgCallback: Function)`
- 用途: 监听消息
- `msgType`: 消息的事件类型
- `msgCallback`: 消息的回调函数,function ({ fromApp, targetApp, type, data }) {}
- `fromApp`: 消息来自于应用的名称
- `targetApp`: 消息目标应用的名称
- `type`: 消息类型
- `data`: 消息内容
#### `$emit(appName: string, msgType: string, msgData: any)`
- 用途: 发送消息
- `appName`: 消息目标应用的名称
- `msgType`: 消息类型
- `msgData`: 消息内容
### 五、常见问题 Q&A
___
1. **这个库到底做了些什么?有什么用?**
- 这个库实际就是postMessage的封装,通过postMessage实现iframe应用与iframe应用间的通信,简化使用者的操作。eg: 如下的应用集成结构
```
主应用 (http://tad.com)
├── 子应用1 (http://aaaa.com)
│ └── 子应用3 (http://bbb.com)
└── 子应用2 (http://ccc.com)
└── 子应用4 (http://ddd.com)
a. 子应用1和子应用2是主应用的子应用,子应用3和子应用4是子应用1的子应用。现在要实现应用与应用间
的通信,比如子应用3发送消息给子应用4。
1. 那就需要子应用先发送消息给子应用1
2. 子应用1收到消息后,再发消息给主应用
3. 主应用收到消息后,再发消息给子应用2
4. 子应用2收到消息后,再发消息给子应用4
b. micro-iframe就是帮你简化这个步骤的,只需要在子应用3中调用$emit发送消息,设置targetApp
为子应用4就可实现,使用者不需要关心消息的传递过程。
$emit('子应用4', 'xxxx', 'xxxx')
```
1. **iframe集成有什么优劣势和其它微前端三方库?**
- iframe的优势在于天然的隔离,集成系统时几乎不会出现太大的问题。而其它微前端三方库在集成时多多少少会出现一些问题要处理。
- iframe的缺点在于:
- 性能问题, iframe的性能开销相对较大
- 渲染问题, iframe需要从头加载资源,渲染速度相对较慢,导致加载期间会有白屏现象
- 资源共享问题,iframe的隔离是好处也是缺点,隔离导致iframe之间无法共享资源,多个应用虽然使用了相同的资源,但都需要重新加载
- 当然以前的问题多多少少都可以通过一些手段进行优化解决的
### 六、版本更新记录
___
- v0.0.1: 首次发布,完成应用间通信基本功能
- v0.0.2:
- 修复声明文件未导出问题
- 添加日志打印开关
- v0.0.3:
- 优化全局对象window挂载声明问题
``` typescript
declare global {
interface Window {
microIframe?: MicroMainApp | MicroChildApp,
microMainIframe?: MicroMainApp
microChildIframe?: MicroChildApp
}
}
```
- v0.0.5:
- 修复在同一域名下不同二级子目录部署的多个子应用通信失效问题
``` typescript
如果项目部署结构如下:
主应用(http://www.xxx.cn/main-app/)
├── 子应用1(http://www.xxx.cn/child-app1/)
├── 子应用2(http://www.xxx.cn/child-app2/)
├── 子应用3(http://www.xxx.cn/child-app3/)
└── 子应用4(http://www.xxx.cn/child-app4/)
注意:多个项目如果是在相同域名下,只是二级地址不一样,origin参数是一样的,因为iframe的origin指的是域名+端口,不包括二级地址
window.microIframe = new MicroMainApp('MAIN_APP', {
name: 'MAIN_APP',
iframeId: 'main-app',
origin: 'http://www.xxx.cn',
children: [
{
name: 'CHILD_APP1',
iframeId: 'child-app1',
origin: 'http://www.xxx.cn',
children: [
{
name: 'CHILD_APP3',
iframeId: 'child-app3',
origin: 'http://www.xxx.cn',
}
]
},
{
name: 'CHILD_APP2',
iframeId: 'child-app2',
origin: 'http://www.xxx.cn',
children: [
{
name: 'CHILD_APP4',
iframeId: 'child-app4',
origin: 'http://www.xxx.cn'
}
]
},
]
})
```
---