微前端体系使用【icestrak】框架融合9个应用,遇到的使用问题和优化心得分享

项目背景

  1. 项目组最初由初创时痛点驱动型平台产品到野蛮生长阶段各类业务平台出现如今要往精耕细作发现上发展——对运营一体化工作台有了新需求,需要收敛用户心智,将多个业务平台融合为单一品牌供用户使用。
  2. 项目组经过多年的迭代,每个项目都有各自的前端技术栈,不同的页面风格和菜单布局,收敛为统一的页面风格迫在眉睫。
  3. 接入低码页面和体系成本过高,无法推动后端全栈化更快的落地,加快和改善前端开发的效率。

image.png
原有单spa应用技术框架对整个项目组业务发展产生了掣肘,2022年中,开始了使用微前端框架对原有多个业务平台进行整合,历时2个月,渐进式的完成了所有项目平台的融合。

简单了解微前端架构设计的理念

下面是我自己梳理的一些微前端理念需要理解的一些基本概念,多用图片表示

image.png

icestark 核心概念

image.png

发布流程

image.png

路由劫持原理

image.png

加载渲染

image.png

应用通信

image.png

微应用加载渲染

image.png

样式隔离方案

我采用 css modules & namespace方案

脚本隔离

通过proxy 创建 window 快照记录与恢复

// 创建
cosnt sandbo= new Proxy(Object.create(null),{
	get(){},
  set(){},
  has(){},
  ....
})
const execScript =`with (shadbox) {;${script}\n}`
const execCode= new Function("sandBox",execScript);
execCode(sandBox);

三方隔离

iframe 方案

项目微前端落地

框架大图

下面是项目脱敏后的微前端体系落地大图,不明所以的部分之后我会一一解释。

image.png

微前端主容器的模块设计&主容器性能优化思路&菜单配置的编排

菜单解析加载和子应用加载解析

1. 我为什么将菜单和子容器加载的逻辑抽离出主容器基座?

项目需要管理N个子应用和之后加入的多个mpa页面和低码开发的单个页面, 菜单更新频繁,不可能频繁的通过打包主容器基座来更新菜单和加载的子应用,所以需要将菜单和子应用配置化抽离出基座,制作成通过接口拉取菜单配置和子应用配置,随着菜单的膨胀,大JSON已经无法很好的满足菜单配置的需求,于是将菜单和子应用配置可视化。可视化后菜单所携带的信息会逐渐增加。

image.png

菜单结构的设计因团队或者公司而异,我主要的思路只配置菜单的menu,子应用的资源配置放到每一项menuItem中,通过循环菜单,生成框架需要的appConfig的资源和入口路径(在icestrak中,配置菜单时候,路由全部设置为绝对路径,相同的子应用会自动匹配资源)。

配置菜单字段设计

整体依据icestark对appConfig做字段扩充,比如国际化标,鉴权字段等。
image.png

关于菜单加载和子应用加载性能优化

上面已经初步搭建完成的微前端体系,但当你跑起一个子应用demo的时候,会发现比自己开发spa应用要慢,这里就要说说微前端应用的渲染体系。

当前的渲染顺序

image.png

从上图可以看出,子应用资源的加载时机相比于spa应用延后的特别多,渲染就会比传统spa应用慢很多,下面是我的两个优化思路

  • 1. 前置预处理子应用资源

image.png

在全局大图中,有一个pre-effect的脚本,第三部分就是上面问题的优化思路,前置子应用资源的加载时机,,在加载主容器资源之前就通过逻辑匹配确定子应用的资源,用浏览器的预请求资源并发机制,提前把首次进入的子应用资源加载好(依据子应用的资源大小提高首屏LCP 200ms~400ms不等)

  • 2. 前置菜单接口和处理菜单逻辑

随着接入的子应用越来越多,菜单膨胀到几千行,此时通过接口拉取菜单JSON配置后,再去生成对应的菜单和appConfig耗时可能超过400ms,微前端应用会出现渲染子应用卡白屏的瞬间,为此,和负责开发网关同学沟通,在生成vm模版时,直接注入到window上全局菜单,引入菜单版本概念,菜单解析完成生成的header menu和silder menu 和appConfig 保存到本地indexDb里,window注入的菜单版本和本地不同,运算一遍菜单生成逻辑,更新indexDB里。

大图中分别为
image.png

image.png

  • 3. 前置处理spa子应用的全局初始化接口逻辑

当子应用的资源加载后,初始化子应用项目时,往往有很多需要默认通过拉取一些通过接口拉取数据后才能初始化完成子应用,往往也会卡很久子应用的渲染,提取这部分初始化接口逻辑到子应用中,并发的调取首次进入的子应用初始化的所需接口,通过webWorker处理,挂载到对应字应用的全局chidlrenConfig上。

image.png

主容器模块功能模块设计思路

【公用模块】全局公用 $.request

9个项目历史跨度巨大,所有融合子项目都要做request改造,子应用处于微应用模式下,子应用统一调用公共请求,封装统一错误处理(统一公共组件ui抛出),增加频繁更新接口缓存机制,设计单项目连续错误上报机制机制。


【公用模块】【性能提升模块】indexDB模块

伪代码如下,做全局信息缓存比如应用的,服务组这种不长更新的,和菜单相关信息缓存

class IndexedDB {
    private db: IDBDatabase | null = null;
    private storeName: string;
    private expires: number | undefined = undefined;// 这里设计的缓存过期时间
    private options: {
      keyPath: string;
    };
    
    
    private init(): void {
        if (!window.indexedDB) {
          console.log('当前浏览器不支持IndexedDB');
          return null;
        }

        const request = indexedDB.open('xxxx',version); // 初始化
        request.onerror = (event) => {
          console.log('打开数据库失败');
        };

        request.onsuccess = (event) => {
          this.db = event.target.result;
        };

        request.onupgradeneeded = (event) => {
          // 创建对应的表
          db.createObjectStore(

          );
          // 创建对应的元数据
          db.createObjectStore('metadataStore', {
            keyPath: 'storeName',
          });

          this.db = db;
        };
   }
   
    public async getData(callback: (data: DataItem[]) => void): void {
        // ....
    })
    
    public async saveData(callback: (data: DataItem[]) => void): void {
         // const transaction  创建  transaction
         // 更新对应的表和元数据信息
         
    })
       
}


【性能优化模块】webWorker 子应用初始化逻辑&接口前置预处理模块

image.png

抽离出来的逻辑和接口,根据子应用初始化逻辑复杂度不等,充分利用浏览器并发机制和通过webWorkder计算不占据主容器渲染线程,,首屏LCP提升100ms~400ms。


【容器逻辑】 初始化菜单部分

这一部分设计的可复杂可简单


【公共模块】全局多语言标

主容器里的i18n标是全局唯一i18n标,子应用的i18n标记跟随主容器刷新,并且负责判定子容器加载 zh/en的language语言包,子应用组件国际化跟随容器标记重置。


【公共模块】Errorboundary module 异常捕获,错误屏蔽以及上报
  1. 前端公共监控SDK的初始化和定制字段上传。
  2. 上传项目指定错误(比如子应用的白屏等)到自己的服务日志,自定义告警通知。
  3. 子应用错误兜底,避免整个页面白屏。

【公共模块】 全局公共模块

使用 @ice/stark-data设计全局通信体系,初始化一些全局公共接口数据到iceStore里。
image.png

【公共模块】系统右侧WidgetSlot插槽模式设计

中台管理系统右侧slot一般有消息通知模块,用户信息模块,全局功能搜索模块和帮助中心模块。
当前主容器基座的设计如果只用在自己的项目中,付出这么多心血其实不值得,如果可以把当前容器设计成企业统一的微前端容器来使用,右侧功能栏的插槽化设计就是最优方案。

这一块实现比较多,需要有动态配置包的能力。
总体思路是将右侧slot抽离成plugin,封装成可插入的plugin插件, vm 初始化时候注入相应的脚本路径和脚本名称,子应用加载后执行插槽的渲染。

【公共模块】主子域名互信token与刷新机制

这部门模块依据我们内部的鉴权系统设计的域名互信,接口请求发起触发主应用和子应用对当前用户的互信,客户端写上互信token,过期时间依据情况项目情况而定,我采用半小时过期,半小时后用户如果没有关闭页面,就重新拉取互信token。这部分逻辑是从一个授权的子应用切到未授权的子应用时触发的。

image.png

主子应用域名互信问题

需要设计一种主域名和子域名互信的机制。
其中这里有难点,就是需要自建网关层,借用服务端proxy转发接口的同时,再请求加上token来解决 接口登陆权限以及跨域的问题。还有另一种思路,可以使用免登陆授权,让子应用的登陆由主应用本身提供,这个需要依据具体情况来设计域名互信机制。


pre-effect.js的设计初衷

在生成vm模版后可以看到,我在容器加载前强行插入一段脚本,这段脚本执行会阻塞容器js的解析和渲染,但是单独抽离出这么一个前置处理脚本是有必要的,如下图,这个脚本做了3件事,
image.png

  1. 通过iframe与请求互信接口,可以前置首屏加载的子应用互信的时机,减少或者避免子应用已经渲染,但互信没有完成的情况。
  2. 第二点,在上面菜单优化加载上已经说明,可以明显的提高首屏的加载速度。
  3. 提前初始化全量变量如环境变量等,让容器里一些依据环境标的逻辑提前运算完成。

iframe 预加载【性能优化方向】

作为中台管理系统,项目本身有很多外部bi报表系统制作的单页面,但加载起来十分慢,通过display:none抽屉来预加载其中的iframe内容,能加载vm上的js和css,很多font和png只有display:block的时候才会再去加载,中间还是会有白屏一段时间。


子应用相关改造和性能优化

spa子应用改造

改造总体思路:保留原本老站点,使用一套代码,增加umd打包格式。工程改造为微前端启动和原有端形式启动,线上资源部署会分为spa打包模式和微前端打包模式,分别独有资源版本号。

spa子应用性能优化

大致说一些优化思路和方向

  • 升级子应用依赖的webpack的版本到5.0以上版本,通过webpack 5.0的tree shake机制,采用esbuld/terser等压缩方式,过滤掉无用的代码块和依赖,拆分弹窗等后置操作为懒加载,脚本的按需引入,优化代码逻辑,减少重复渲,大数据量数据处理通过webWorker 处理等方式减少首包体积,加快子应用的解析提升LCP。
  • 通过微前端主容器,对子应用资源做缓存。
  • 对css不合理的地方就行调整,减少屏幕的抖动,尽量减少整个页面的重绘行为。
  • memory分析内存的占用情况,找到一些内存泄露的点,来减少浏览器的负载。
  • 提取几个子应用的公共依赖,某一个子应用加载公共依赖后,后续切换到的子应用就可以走本地缓存,减少其他子应用的首包体积。
  • 静默刷新的能力
  • 尝试统一子应用的字体

新页面

微前端体系让mpa应用重新占据优势,新页面都通过mpa应用打包生成单页面引入微前端体系。

最后

下午有时间不太忙的话持续更新~,欢迎评论区交流微前端体系使用经验和优化心得,提出补充建议。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYBESCKj' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片