微前端之桥接方案, 打破技术栈次元壁-实现无缝对接
简介#
1. 微前端桥接方案是什么?#
是一种新的微前端解决方案,使用了一种巧妙的方法去实现了微前端架构,只需通过调用高阶函数即可实现不同技术栈之间的互通
2. 何谓无缝?#
微应用接入与原生技术栈应用毫无差异,不需要任何额外的信息
3. 为什么需要,是否重复造轮子?#
在项目实践落地过程中,调研过和尝过不同的落地方案要么是兼容性问题,要么就是项目改造有点重,css 样式隔离也是一个头疼的问题。于是 bridge 方案就此诞生了.
现在有很多微前端方案,为什么要用 bridge 方案?先来总结和梳理一下目前主流的几种方法:
1. 基于 Webpack 5 的 Module Federation
优点:可实现第三方依赖共享,减少不必要的代码引入;微应用可动态更新,无需重新打包发布整合应用;去中心化,每个微应用间可直接通信。
缺点:对 webpack5 的依赖度较高,项目若不使用 webpack5 则难以接入。
2. 基于 iframe
优点:实现简单,无需对现有应用进行大量改造;能提供浏览器原生的硬隔离,JS、CSS、DOM 都完全隔离开,互不影响;可通过 postMessage API 进行消息传递。
缺点:无法保持路由状态,刷新后路由丢失,浏览器前进后退功能受限;应用间上下文难以共享,交互困难;弹窗只能在 iframe 内部展示;全量资源加载,性能较差;不利于 SEO。
3. 基于 Web Components + 沙箱
优点:CSS 和 JavaScript 天然隔离,避免样式冲突和脚本污染;原生浏览器支持,不依赖特定框架或库;多个子应用可并存,支持并行开发和独立部署。
缺点:浏览器兼容性存在问题,部分浏览器不完全支持;开发成本较高,可能需要重写现有应用;组件间通信需要额外设计。
4. sigle-spa + 沙箱
优点:技术栈无关,任意技术栈的应用均可接入;采用 HTML entry 接入方式,接入成本低;提供了样式隔离、JS 沙箱、资源预加载等功能。
缺点:样式隔离问题不完善,打包工具有一定限制等,组件通信问题等…
基本上不是样式隔离出了问题,就是兼容性出了问题….
bridge 方案和其他微前端方案的区别是什么?#
1. 使用更简单,使用更自然,无额外知识,通过高阶函数创建桥接组件并直接使用
2. 无 iframe 和 shadow dom 的依赖,兼容性更好
3. 组件通信更自然 (通过原生 props 属性可进行父子组件通信)
4. 可组件化导入子应用 / 子组件
5. 样式隔离问题处理更优 (采用 css-module 或者 scoped 隔离方案取决于项目自身打包工具的配置)
那如何证明上述结论呢?废话不多说直接上代码.
代码演示#
以 react 18 为主应用作为举例,如果要接入 vue2#
1. 第一步创建桥接应用
//假设子应用路径为 .children-app/accesstor/Button
import vue from 'vue'
import { createVueBridge } from 'micro-frontend-bridge/for-react'
import Button from './Button.vue'
//假设子应用是一个vue2的项目
//为vue2创建一个桥接访问器
const accesstor = createVueBridge(vue)
//访问器是一个高阶函数,用来链接vue button按钮
export default accesstor(Button)
第二步输出桥接组件
通过打包工具将 button 打包成 lib 输入到主应用里
第三步主应用使用桥接组件
//假设主应用有一个main.jsx文件和bridge文件夹存在桥接组件
//主应用是一个React 18 项目
import React from 'react'
import Button from 'bridge/Button'
//在react18中使用vue2的按钮 推荐使用大写组件名称区别桥接组件
const BUTTON = Button(React)
const App = () => {
return (
<div>
<BUTTON color="grey" />
</div>
)
}
没错这个时候 react 18 和 vue2 已经无缝连接了,那么如何进行组件通信呢?
组件通信也是非常自然的,直接基于 props 传递属性即可,代码中的 color 属性会传递给 vue2 的 button, 通过回调函数也可以进行父子组件通信与原生通信模式无差异. (末尾有在线地址可以直接实践).
总结一下就是 :
- 创建桥接组件
- 输出组件
- 使用桥接组件
在这里其实第二步打包组件输出 lib 的过程根据项目架构的特点是可以省去的,可以把桥接组件放在主应用内,对主应用添加子应用的打包支持,如果你的主应用用的是 webpack, 那么对子应用添加 vue 打包支持即可,就可以省略配置打包 lib 这一步了
示范如下,调整桥接组件目录到主应用
//假设桥接组件应用路径为 bridge/Button
import vue from './children-app/node_modules/vue'
import { createVueBridge } from 'micro-frontend-bridge/for-react'
//这里路径引入做了调整
import Button from '../children-app/Button.vue'
//假设子应用是一个vue2的项目
//为vue2创建一个桥接访问器
const accesstor = createVueBridge(vue)
//访问器是一个高阶函数,用来链接vue button按钮
export default accesstor(Button)
调整 webpack 打包支持
{
test: /\.vue$/,
include: [path.resolve(__dirname, 'children-app')],
use: {
loader: './children-app/node_modules/vue-loader/lib',
options: {
compiler: Vue2TemplateCompiler
}
}
},
使用组件
//假设主应用有一个main.jsx文件和bridge文件夹存在桥接组件
//主应用是一个React 18 项目
import React from 'react'
import Button from 'bridge/Button'
//在react18中使用vue2的按钮 推荐使用大写组件名称区别桥接组件
const BUTTON = Button(React)
const App = () => {
return (
<div>
<BUTTON color="grey" />
</div>
)
}
(具体的演示看末尾线连接)
如何优化这个微前端应用?
#
一般来说微前端接入项目由于不同技术栈的存在,可能会导致包体积过大的问题,由于本身 bridge 方案本身是基于高阶函数实现的,使用动态 import 和 suspense 就可以实现按需加载达到优化的目的了.
最后以 vue2 和 vue3 做为主应用作为举例子,如果接入 react 组件#
import React from 'react'
import ReactDOM from 'react-dom'
import { h } from 'vue'
import { createReactBridge } from '@micro-frontend-bridge/for-vue'
import { App } from './reactApp.tsx'
// create vue3 accessor
const v3reactAccessor = createReactBridge(React, ReactDOM)
// create vue2 accessor
const v2reactAccessor = createReactBridge(React, ReactDOM)
//bridge react app to vue3
const V3APP = v3reactBridge(h)(App)
//bridge react app to vue2
const V2APP = v2reactBridge()(App)
sfc file
<template>
<V2APP />
</template>
<template>
<V3APP />
</template>
在线演示#
仓库地址#
hanyaxxx/micro-frontend-bridge
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: