背景
随着公司业务的不断扩大,前端应用的规模也越来越大,单体应用的维护成本越来越高。微前端的概念应运而生,可以将一个大型的前端应用拆分成多个小型的应用,每个应用都可以独立开发、部署、运行,从而降低了应用的维护成本。这就需要选择一个合适的微前端框架来解决这些问题。
技术选型
市面上常见的微前端框架有乾坤、无界等。实现方式上乾坤是基于single-spa方案,使用function + proxy + with实现,无界是基于WebComponent + iframe来实现。下面我们对比了乾坤和无界这两个框架的优缺点。
| 框架 | 优点 | 缺点 |
|---|---|---|
| 乾坤 | 资源预加载; 具有完备沙箱方案来隔离js和css; HTML Entry 接入方式 | 基于路由匹配,无法同时激活多个子应用,也不支持子应用保活; 改造成本较大,从 webpack、代码、路由等都要做一系列的适配; css 沙箱无法绝对的隔离,js 沙箱在某些场景下执行性能下降严重; |
| 无界 | 具备qiankun的所有优点,另外还具有以下优点: 主应用使用成本及子应用适配成本低; css沙箱和js沙箱都采用了原生隔离,无需担心污染问题; 支持路由保活和共享依赖; 插件系统强大,方便在运行时修改子应用代码; | 目前还比较新, 社区不够活跃 |
两个方案一个是阿里出品,一个是腾讯出品,都是挺优秀的微前端解决方案。考虑到改造适配成本我最终采用了无界方案。我们通过到家低代码平台配置的子应用页面,主应用通过无界框架去加载子应用页面。
graph TD
到家低代码平台 --> 子应用页面 --> vue主应用
具体实现(10行代码搞定)
1、安装,引入npm包,我们是vue项目,所以引入Vue组件封装包
# vue2 框架
npm i wujie-vue2 -S
# or vue3 框架
# npm i wujie-vue3 -S
2、引入
// vue2
import WujieVue from "wujie-vue2";
// vue3 引入下面的模块
// import WujieVue from "wujie-vue3";
const { bus, setupApp, preloadApp, destroyApp } = WujieVue;
Vue.use(WujieVue);
3、使用
<!-- 参数说明 url: 子应用url链接, props: 往子应用传参 -->
<WujieVue
name="orderBtns"
:url="wujieBtnUrl"
:fetch="fetch"
:props="wujieProps">
</WujieVue>
至此我们主应用改造完成,是不是10行代码左右。看下效果,左边的按钮是无界子应用引入的按钮,右边是页面原有按钮,融合效果还是比较好的。
优化
发现一个问题,无界子应用加载的按钮和右边的按钮展示的时候有先后顺序出现,体验上咱过不去呀,能不能让其同时出现。无界提供的预加载能力+事件通信机制保障。代码也比较简单
// 预加载子应用
preloadApp({
// name必须和前面WujieVue的name一致
name: 'orderBtns',
fetch: this.fetch,
url: this.wujieBtnUrl,
props: this.wujieProps
});
// 通过bus通信,等待子应用的显示通知
bus.$on('order:btn:show', () => {
this.showWujieBtn = true;
});
无界能力很强大,远远不止于此
能不能无界子应用里面调用其他业务系统模块,答案是可行的。这里我们就要用到无界的插件机制。官方对无界插件的解释是:无界的插件体系主要是方便用户在运行时去修改子应用代码从而避免去改动仓库代码,从下面这张图我们能看出来覆盖范围还是挺全的。
借助无界的插件机制那我们的改造就比较简单了
<!-- WujieVue添加插件配置 plugins -->
<WujieVue
name="orderBtns"
:url="wujieBtnUrl"
:fetch="fetch"
:plugins="wujieConfig.plugins"
:props="wujieProps">
</WujieVue>
我们想在html中所有的js之后执行逻辑,用到js-before-loaders这个配置,官方给的示例如下:
- 在子应用运行一个
src="http://xxxxx"的脚本 - 在子应用中运行一个内联的 js 脚本
<script>content</script> - 执行一个回调函数
this.wujieConfig = {
"plugins": [
{
// 加载之后
jsAfterLoaders: [
// 插入一个外联脚本
{ src: "http://xxxx.js" },
// 插入一个内联监本
{ content: 'console.log("test")' },
// 执行一个回调,打印子应用名字
{
callback(appWindow) {
// 执行回调逻辑
},
},
],
}
]
};
最终我们完美实现了在子应用里面弹出我们想要嵌入的模块弹框,效果如下:
总结
综合体验下来,感觉无界能力很强大,比如我们可以基于无界把一些复杂页面拆解成楼层搭建配置模式,从而带来的研发效率以及页面稳定性的提高收益还是不错的。无界方案目前已经在到家业务订单系统中有落地。