微前端
微前端(Micro Frontends)是一种架构风格,它将大型前端应用程序分解为一组更小、更易于管理的独立前端应用程序。这些小型前端应用程序(通常称为“微应用”)可以独立开发、测试、部署和维护。
解决公司历史遗产代码兼容性问题
特点
独立性:每个微应用可以独立开发和部署,不受其他微应用的影响。
技术栈无关性:不同的微应用可以选择不同的技术栈(例如,React、Vue、Angular)。
可扩展性:新的微应用可以很容易地添加到系统中,以支持新功能或业务需求。
团队自治:每个微应用通常由一个独立的团队负责,团队可以自主选择开发工具、流程和发布计划。
共享生态:微应用之间可以共享一些公共的库、组件和服务,以保持一致性和减少重复工作。
接口驱动:微应用之间通过定义清晰的接口(如 API、Web Components)进行通信和数据交换。
独立运行:每个微应用可以作为一个独立的单元运行,也可以与其他微应用组合在一起。
解决方案
iframe
- 优点:
- 简单
- 隔离
- 无限制使用
- 缺点:
- 每次进入都要加载,状态不保留
- 通信不方便(postMessage)
- 无法资源共享,整个应用全量资源加载,加载太慢(没有办法复用)
模块联邦
模块联邦(Module Federation)是一种 Webpack v5 引入的先进技术,它允许你将多个 Webpack 应用组合成一个更大的应用。模块联邦的核心思想是让独立的 Webpack 应用能够互相加载对方的模块,从而实现应用之间的资源共享和通信。
- 优点:
- 可以在a系统中直接复用b系统的组件
- 每个应用也是独立开发打包部署
- webpack构建系统可以优化模块联邦应用的构建过程,提高效率
- 缺点:
- 得用同一种语言比如vue、或者react,如果跨语言调用组件会很麻烦
web components
- 优点:
- 技术栈无关:是浏览器原生组件
- 独立开发
- 应用隔离
- 缺点:
- 语法强制(强制使用某一种技术栈)
服务端拼接
配置nginx代理
ESM + importmap
ESM引用必须要用"/"或者"./"开头
浏览器原生支持
微前端框架
single-spa
- 加载微应用(自己实现加载方法)
- 管理微应用的状态(初始化、挂载、卸载)
qiankun
基于single-spa实现的,孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台。
实战应用
前段时间紧急演示搞了一下嵌入式,基本上把各种疑难杂症算是解决了一圈,所以还是要记录一下。当然有人说不如无界wujie
好用,所以讲完qiankun
马上讲wujie
。
- 这里使用vite vue3架构来实现qiankun嵌入,将一个子系统完全嵌入主系统,并且实现单点登录。
主程序
- 首先安装
qiankun
pnpm install qiankun
- 然后新建一个
qinakun
的文件夹,里面放一个index.ts
,当然你丢到哪里都无所谓,比如我其实在util
文件夹里面放了一个qiankun.ts
内容如下:
import { runAfterFirstMounted } from 'qiankun'
runAfterFirstMounted(() => {
console.log('qiankun开机')
})
注意:我们这里演示的是缓存写法,所以不在全局注册所有的子系统。
然后在main.ts
中引用这个文件。
- 因为子系统要嵌入到某个页面的内部,所以我们提前定义好页面组件,并且配置好路由。
这个子组件SubSystem.vue
是这么写的:
<template>
<div class="son-system" v-loading="loading">
<div id="subSystem" style="height:100%"></div>
</div>
</template>
<script setup>
import { start, loadMicroApp } from 'qiankun'
const isDevelopment = import.meta.env.MODE === 'development'
const loading = ref(true)
// 获取子系统传递的消息
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
setTimeout(() => {
loading.value = false;
}, 500)
});
onMounted(() => {
if (!window.qiankunStarted) {
// 这里写你子系统的地址
const url = (isDevelopment ? "http://localhost:5174" : "/subSystem");
loadMicroApp({
name: "subSystem", //子应用名称
entry: url, //子应用地址
container: "#subSystem", //子应用要挂载的节点,和vue.$mount("#app")类似;
});
window.qiankunStarted = true
start();
}
})
</script>
<style lang='scss' scoped>
.son-system {
width: 100%;
height: 600px;
}
</style>
子程序
首先给子系统安装一下
vite-plugin-qiankun
,因为vite不被qiankun支持,但是有大佬做了插件兼容了。然后先去
vite.config.ts
中,做一些配置如下:
import qiankun from 'vite-plugin-qiankun'
export default ({ command, mode }: ConfigEnv): UserConfigExport => defineConfig({
plugins: [
qiankun('subSystem', { // 子应用名称一致
useDevMode: true
})
]
})
- 接着去
main.ts
中,做一些配置:
import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'
const render = (props: QiankunProps = {}) => {
const { container } = props
const appEle: string | Element = container?.querySelector('#app') || '#app' // 避免 id 重复导致微应用挂载失败
// 注意把原来的挂载提进来
app.mount(appEle)
}
const initQianKun = () => {
renderWithQiankun({
bootstrap() {
console.log('我是微应用:bootstrap')
},
mount(props) { // 获取主应用传入数据
console.log('我是微应用:mount', props)
props.setGlobalState({
msg:"i am OK"
});
render(props)
},
unmount(props) {
console.log('我是微应用:unmount', props)
},
update(props) {
console.log('我是微应用:update', props)
},
})
}
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render() // 判断是否使用 qiankun ,保证项目可以独立运行
- 再去
router
中该路由,如下:
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
const router = createRouter({
history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__
? '/subSystem'
: import.meta.env.BASE_URL),
})
- 最重要的来了,去主程序里面拦截路由:
router.beforeEach((to, from, next) => {
// 主程序清除由于子程序路由导致的变化
if (to.href.includes('/subSystem/')) {
next({ path: '/subSystem' });
return
}
...
...
...
}
- 其他问题
- 缓存问题
- 之所以用手动挂载就是为了解决缓存问题
- 需要主系统和子系统分别设置keepalive
- 单点登录
- 主系统登录的时候调用子系统的登录方法并按照对应规则存储
无界wujie
上 链接
官网东西很好,不做拓展了
主应用
- 找个页面随便写写代码,就结束了,如果都配置了
keepalive
的话,缓存也可以正常使用。-
<template>
<div class="son-system">
<WujieVue width="100%" height="100%" name="xxx" url="http://127.0.0.1:5176/sonApp/" :sync="true"
:props="{}" :beforeLoad="beforeLoad" :beforeMount="beforeMount" :afterMount="afterMount"
:beforeUnmount="beforeUnmount" :afterUnmount="afterUnmount"></WujieVue>
</div>
</template>
<script setup>
import WujieVue from "wujie-vue3";
const beforeLoad = (e) => {
console.log("beforeLoad", e)
}
const beforeMount = (e) => {
console.log("beforeMount", e)
}
const afterMount = (e) => {
console.log("afterMount", e)
}
const beforeUnmount = (e) => {
console.log("beforeUnmount", e)
}
const afterUnmount = (e) => {
console.log("afterUnmount", e)
}
</script>
<style lang='scss' scoped>
.son-system {
width: 100%;
height: 600px;
margin: auto;
}
</style>
- 设计其他的通信等可以去官网找api,这里不做赘述。
- 至于单点登录很简单,不做赘述,用通讯就行
整个系统按理上传到了 这里 ,自行下载查看
优势
微前端与iframe相比优势在哪:
- 搜索引擎优化(SEO)对微前端更友好,因为dom元素存在于document中
- iframe 会为每个内嵌页面创建一个独立的文档和全局作用域,这可能导致额外的资源消耗和性能问题
- iframe 的集成方式较为固定,限制了页面间的交互和通信
- iframe 内嵌的页面通常不支持SPA的路由方式,微前端架构可以与SPA路由无缝集成,提供无刷新的页面导航体验
- iframe 的缓存只能通过隐藏的形式,系统多的时候内存消耗过大
- 从6开始主要说wujie框架的优势,preload,空闲时候可以预加载
- 额外提供了插件的能力,例如修改子应用所有的aaa为bbb,按需加载js,替换js,提前执行自定义js,延后执行js
- css也是同理
micro app
补充一下微前端技术,这个技术来自于京东,这个跟wujie有点像,上 链接
缺点:
- 1.0之前不支持vite,1.0之后支持
- 子应用和主应用必须相同的路由模式,要么同时hash模式,要么同时history模式