Appearance
微前端
微前端受到了微服务的启发和借鉴,从而在前端领域提出了一种类似于微服务的思想。微服务的主要特点是将应用程序拆分成小型、自治的服务单元,并通过轻量级的通信机制相互协作。微前端也将前端应用程序拆分成小型、自治的模块,并通过各种技术手段相互协作。
虽然微前端和微服务有一些相似之处,例如都将应用程序拆分成小型的自治单元,但是它们的主要区别在于应用程序的拆分粒度不同,微服务更注重服务的拆分,而微前端更注重前端应用程序的拆分。
解决问题
传统前端存在的一些问题:
- 所有的代码都集中在一个代码库中,修改一个模块可能会影响其他模块的功能。不同的模块之间的依赖关系复杂,导致开发人员需要花费更多的时间来理解和维护复杂的代码结构。
- 单体式应用程序的部署通常需要将整个应用程序重新构建和部署,这样会增加开发和运维的复杂性,并且会使部署过程变得更加缓慢。
以上问题随着项目迭代会越来越明显,随便发个版可能都要半个小时之久。
而微前端可以将前端应用程序拆分成小型、可独立开发和部署的模块。每个模块都有自己的代码库、技术堆栈和团队,可以独立地开发和维护,这种模块化的架构能够提高应用程序的可扩展性和灵活性。
实现方式
- Web Components:Web Components 是一种 HTML 标准,可以让开发人员创建自定义、可重用的组件。微前端可以使用 Web Components 来封装应用程序的不同模块,并在应用程序中进行组合和替换。
- iFrame:iFrame 可以将一个网页嵌入到另一个网页中。微前端可以使用 iFrame 将不同的应用程序模块嵌入到主页面中,并通过消息传递机制相互通信。
- 模块联邦:模块联邦是一种新的微前端技术,它允许各个独立的应用程序共享模块。模块联邦的实现方式包括基于 Webpack 的 federated module 和基于 SystemJS 的 dynamic import。
- 跨应用路由:微前端可以使用跨应用路由来实现不同应用程序之间的导航和跳转。例如,可以使用 Single-SPA(一个微前端框架)来管理不同的应用程序之间的路由和状态,另一个 qiankun 基于 Single-SPA 的微前端框架,提供了更加简单易用的 API,并支持模块联邦等新兴技术。。
iframe
postMessage 方法允许在不同的窗口或 iframe 之间发送跨源消息。可以通过监听 message 事件来接收消息,从而实现宿主页面和子应用程序之间的双向通信。发送的内容可以是任意可序列化的 JavaScript 对象或基本数据类型,这意味着循环引用、函数、日期对象等将无法被发送。
容器应用
发送消息
发送消息
vue
<template>
<p>容器应用</p>
<el-row :gutter="8">
<el-col :span="12">
<p>发送消息</p>
<el-input v-model="send_msg" type="textarea" />
<el-button type="primary" @click="onSend">发送</el-button>
</el-col>
<el-col :span="12">
<p>发送消息</p>
<div class="h-100 scroll-y" v-html="receive_msg"></div>
</el-col>
</el-row>
<iframe
ref="childApp"
class="h-200 fill-w"
src="https://fjxylin.gitee.io/pages/iframe-postMessage.html"
></iframe>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const send_msg = ref()
const receive_msg = ref('')
const childApp = ref()
// 此处做演示将目标 origin 设置为 *,在生产中请始终指定精确的目标 origin,例如 http://notes.fjxylin.cn
const onSend = () => childApp.value.contentWindow.postMessage(send_msg.value, '*')
// 生产中始终使用 e.origin 和 e.source 属性验证发件人的身份
onMounted(() => {
window.addEventListener('message', e => {
console.log('容器接收:', e)
if (e.source === window) return
receive_msg.value += `<div>${e.data}</div>`
})
})
</script>
注意
使用 postMessage 发送消息请始终指定目标 origin,避免窗口位置被更改,被恶意拦截 postMessage 发送数据。
监听 message 事件时,请校验发送方窗口的源(origin),并只使用 source 进行回复。
qiankun
qiankun 是一个基于 single-spa 的微前端解决方案。
基本逻辑如下:
- 在父应用中通过
registerMicroApps
注册子应用; - 在应用入口出需要导出三个生命周期钩子
bootstrap
,mount
,unmount
,根据window.__POWERED_BY_QIANKUN__
判断是独立运行还是微前端运行,并修改打包模式为umd
; - 匹配路由并加载子;
提供了更多的功能与工具:
- 路由共享:qiankun 可以让多个子应用共享同一个父应用的路由,使得整个系统更加统一和协调。
sh
http://notes.fjxylin.cn # 主应用
http://notes.fjxylin.cn/app1 # 子应用
http://notes.fjxylin.cn/app1/... # 子应用
- 状态管理:qiankun 提供了一种基于 props 的状态管理方式,可以让父应用将状态传递给子应用,并监控子应用状态的变化。
js
// 主应用在注册子应用时传递 props
registerMicroApps([{ /* ... */ props: { username: 'qiankun' } }])
// 在子应用导出的 mount 生命周期中获取
export function mount({ props }) {}
- 样式隔离:
css module
通过限制 css 选择器范围控制样式;将子应用放在shadow dom
影子节点下。
html
<style>
.box[data-v-2dcfc13e] {
}
</style>
<div class="box" data-v-2dcfc13e></div>
js
// 创建 shadow dom
const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' })
// 创建子应用需要的挂载节点
const appContainer = document.createElement('div')
appContainer.id = 'app'
shadowRoot.appendChild(appContainer)
document.body.appendChild(shadowRoot)
js 沙箱:保证各个子应用之间的代码隔离和安全性,引入了 js 沙箱机制,分别是
快照沙箱(snapshotSandbox)
,代理沙箱(proxySandbox)
;快照沙箱:
- 在激活沙箱时,将当前
window
保存到snapshot
中,并将modify
信息还原到当前window
上; - 退出沙箱时,将当前
window
与snapshot
做 diff,将信息保存至modify
中,并将当前window
还原至snapshot
状态;
- 在激活沙箱时,将当前
快照沙箱作为最简单的沙箱,可用于不支持
proxy
代理的环境,但操作过程对window
有污染。代理沙箱:
- 在激活沙箱时,创建一个变量
fakeWindow
; - 在读变量时,优先读取
fakeWindow
,如果未找到则从window
上读取;设置时直接操作fakeWindow
;
- 在激活沙箱时,创建一个变量
代理沙箱使用
proxy
代理window
,操作过程中没有污染window
,相对来说快照沙箱来说做到了正真的隔离。
iframe 与 qiankun
- 路由上:iframe 中子应用路由变化无感知,qiankun 中子应用与主应用共享路由,更像是一个整体;
- 状态管理:iframe 需要自己实现一套状态管理,qiankun 内置了功能,通过注册应用与生命周期方式传入;
- 应用通讯:iframe 借助 postMessage 完成通讯,qiankun 可使用事件总线来通讯,更加简单与灵活;
- 环境隔离:iframe 将子应用嵌入到主应用中,利用浏览器的安全沙箱机制隔离了不同子应用之间的代码和数据,qiankun 利用模块来加载子应用,并利用 css 隔离与 js 沙箱等技术来实现子应用之间的隔离和通信。