博客 / 詳情

返回

Module Federation在客服工單業務中的最佳實踐

Module Federation: 是模塊聯邦的意思,在webpack 5中流行起來的,也屬於一種微前端方案。

一、背景

1、客服高頻工作場景

一線客服: 基於一站式工作台中的在線工作台及電話工作台,根據用户進線反饋的問題,查看當前用户相關的工單詳情或訂單詳情,並根據實際情況決定是否創建新的工單。

二線客服: 根據各種渠道反饋的工單(其中一個主要來源是一線客服的反饋),根據工單內容,聯繫用户或其他相關方溝通處理並完結工單。

2、使用iframe的背景

在上述場景中我們可以知道:

  • 一線客服和二線客服不是同一批同學,分屬不同的部門;
  • 由於業務上的一些區別,所以IM與工單是兩個不同的前端項目,也由不同的前端同學開發和維護;
  • 即使使用微前端拆分後,IM、電話和工單也屬於不同的微應用,工單頁面在IM和電話子應用中沒法直接引入工單詳情頁面

改造之前 IM 和電話工作台中使用到的工單 詳情 頁面都是使用iframe嵌套的,這在前期是最簡便,兼容性最好的方案。

二、存在的問題

1、iframe問題的擴大化

我們知道,單個iframe是比較佔 內存 的,打開比較慢,只是偶爾使用的時候,這些可能並不是什麼大問題,但在一線客服中有這麼些場景會急劇擴大這些問題,具體如下:

  • 一線客服與單個用户對話過程中,打開多個該用户的工單詳情或訂單詳情
  • 一線客服對接多個用户諮詢
  • 一線客服頻繁且快速的切換不同的用户及不同的工單/訂單詳情
  • 由於客服對切換不同會話的響應有要求,IM側對不同的會話頁面做了緩存(如keep-alive)

這會導致 內存 消耗急劇升高,切換響應速度慢,甚至會導致瀏覽器崩潰,客服使用 體驗 差,最終導致客服效能的降低

2、內存不斷升高

模擬用户在IM中打開一個新客户的消息,點擊創建工單,點擊工單詳情,再打開一個新的客户消息重複上述動作的頁面文檔數,內存佔用及頁面節點數的趨勢圖,如下:

由於頁面緩存的存在,可見 內存 佔用是越來越高的,在一分多鐘的時間內從100多MB到500多MB,而且可以判斷的是當用户的這類操作,其內存佔有率將持續走高,雖然在大多數情況下瀏覽器的垃圾回收機制會啓動,但由於:

  • 頁面級的緩存使垃圾回收不那麼有效
  • 在客服的長時的工作下,內存還是會持續走高

最終應用會越來越慢,甚至導致崩潰,並已經有一些客服在反饋崩潰的問題了:

3、響應速度慢

先看段工單詳情打開的視頻,如下:

視頻中打開工單詳情並非首屏打開,也需要2.8秒,當首屏打開的時候甚至需要7s!

注意: 此處及後續的數據都是在測試環境採集的,由於是內部實現的新開標籤頁,對LCP( Largest Contentful Paint, 最大內容渲染 ), TTI( Time to Interactive, 可交互時間)等不能很好採集,主要是根據谷歌性能工具中截屏中的時間數據來獲取的,該值更接近於TTI。

三、選型及對應策略

根據當下的情況,已經不能再繼續使用iframe了,那有什麼方案可以滿足我們目前的情形呢,首先梳理我們目前的訴求,這個訴求主要有兩方面:

  • 一方面當然是解決上述問題,提升性能,提升客服使用體驗
  • 另一方面它需要對前端來説要足夠友好,能實現,有良好兼容性,也要保持前端的目前低成本維護

擺在我們面前的常用的方案大概有這麼兩個,npm包和以qiankun為代表的微前端

方案 性能 維護
npm 好,與本地組件無異,性能是最好的 差,一旦工單有發佈,IM也得跟隨發版,不可接受
微前端 較好 好,項目解耦,IM側無需關心工單側的迭代及具體技術

1、npm包適合麼

先看下我們需要引用的頁面本身,如下圖所示:

它是一個複雜度高的,業務關聯性高,更新頻次高的三高頁面級組件,完全不適合做成低頻的npm包了

2、微前端合適麼

大家可能以為我要選qiankun為主的微前端方案了,但並不是,因為qiankun或其他類似的微前端方案在我們這樣的具體場景下也有一些問題:

2.1、對嵌套場景的支持度不高

在我們一站式工作台的整體方案中,已經使用qiankun將工單、IM、電話拆成了三個子應用,如果IM再引用工單子應用就會形成應用之間的嵌套,如下圖所示:

搜尋微前端嵌套方案:

  • 社區沒有官方支持嵌套方案:qiankun 微前端實踐總結(二) - 掘金 這篇文章這段闡述了嵌套的方案,可以看出雖然官方沒有推薦方案,但是社區已經有相關實踐,但是支持度不夠好,而且這些實踐是基於在webpack構建基礎上的;
  • qiankun對vite不友好:見issue想問一下,未來是否考慮支持 vite · Issue #1257 · umijs/qiankun,只是最近才慢慢的增加了對vite的支持。

結論 無論是webpack還是vite,以沙箱隔離為主要解決方案的微前端在嵌套場景上支持度不高 並且如果以後出現更多,更深,甚至循環嵌套的情況的話,那就更棘手了。

2.2、場景不適合

以沙箱隔離的微前端方案,還是會在每次引用時將項目本身初始化所需的文件都引用並且解析加載,即使你引用一個小組件他也會這麼做,還是會影響組件加載的速度。所以它主要是解決以項目維度的引用,隔離及性能問題。在以模塊或組件為維度的情形不適合,它還是有點重了。

那麼是否存在這樣一個方案,可以像npm包這樣靈巧,只加載對應的組件代碼,又可以像微前端這樣直接引用,與項目無關呢,答案是有的,那就是這次的主角——Module Federation(模塊聯邦)。

四、Module Federation模塊聯邦

模塊聯邦,它首先是在webpack 5中流行開來的,可見文檔:Module Federation | webpack 中文文檔,而且它也屬於一種微前端方案。

1、我們關注下它能幹什麼

我感覺一句話概括就是:它能允許一個線上部署的項目在運行時加載其他線上部署項目中的組件

其中關鍵幾點如下:

  • 它沒有沙箱隔離方案,與宿主環境共用一個window環境
  • 加載時只加載目標組件及其相關的內容
  • 無需改變組件或頁面在之前的項目目錄位置或結構,直接根據對應目錄層級引用即可
  • 使用時與本項目組件使用方式一致,因為它就是一個組件,只是遠程加載而已

2、基本原理

設想一下,在webpack中可以將一些後續加載的模塊打成chunk包,然後在使用到這個模塊的時候再懶加載,這種情形一般是在同一個項目中進行。

那麼我們是否也可以在A項目中把一個組件及其的依賴打包成chunk包,而在B項目中按約定的地址異步import剛才那個A項目的chunk包,並運行使用呢?答案就是Module Federation。

2.1、一些概念

  • 遠程組件提供方稱為remote端,遠程組件使用方稱為host端。
  • 一個項目既可以為remote端,也可以為host端,可以使用數個不同其他項目的組件,也可以為數個其他項目提供不同的組件。

據此,我們甚至可以打造一個去中心化的應用集羣,大概這樣:

2.2、社區應用

而且此項技術已在多個知名廠商中應用:

  • 探索 webpack5 新特性 Module federation 在騰訊文檔的應用 - 掘金 (騰訊)
  • 最全彙總之微前端知識和實戰(EMP技術方案) - 掘金 (YY)

2.3、vite中的Module Federation方案

vite插件vite-plugin-federation提供了對標webpack的能力,只是加載的方式改成了ESM,其插件接口設計與webpack的參數設計幾乎是一樣的。vite-plugin-federation也是vite官方推薦和收錄的插件,已收錄在社區插件列表GitHub - vitejs/awesome-vite: ⚡️ A curated list of awesome things related to Vite.js。

2.4、具體到我們的場景中

工單工作台應用,作為remote端,它可以提供工單詳情,訂單詳情和創建工單,賠付模塊等多個頁面級組件。

IM 或電話工作台應用,作為host端,可以使用上述組件。

3、具體實踐

由於我們兩個項目都使用的是vue3 + vite方案,所以使用vite-plugin-federation插件是我們最佳的選擇。首先需要在host端和remote端安裝插件,如下命令:

yarn add @originjs/vite-plugin-federation -D

注意: 此處為了展示方便使用了官方插件,實際由於項目相關的原因,我們對官方插件進行了一些改動。

3.1、工單工作台作為remote端的配置

(1) 在插件內註冊需要提供的組件

並且提供需要共享的三方依賴

(2) 配置完成後打包

在remote端打包後會生成對應的入口文件,chunk文件及依賴包文件:

3.2、IM和電話工作台作為host端的配置

(1) 插件配置,設置遠程包地址

(2) 引用及註冊組件 main.ts文件 如下

這時需要注意,ticket-share為host側在vite插件中註冊的包名,Detail為remote端暴露(exposes字段)聲明的組件名。 除了在全局註冊,還可以在任意需要引用的地方引用。

(3) 組件的使用

在需要的地方引入使用,並根據業務情況傳遞props,props的使用方式與普通組件無異。

此時我們已經完成了全部配置,此後我們只要打包上線運行即可。

五、重構之後的效果

1、加載速度

再來看一段視頻

大家可以與最開始的視頻對比一下,可以看出非常直觀的差異。

首屏 二次(非首屏)
iframe 7076ms 2594ms
模塊聯邦 1279ms 428ms

注意:上述數據由測試環境進行十次平均得到,測試值更接近TTI。

從數據中可以看到,在首屏環境(即無任何緩存)下模塊聯邦加載速度提高了5.5倍。而在二次加載場景中(此場景是客服使用頻率最高的場景),速度提高了6倍!

2、內存

我們依舊根據開始的場景,模擬客服打開多個用户多個工單/訂單詳情的情形,如上圖,首先可以發現:

  • 沒有內存泄漏!
  • 內存佔用在相同的時間內,最高只達到了55MB,並回落。
  • 單個工單詳情打開內存增幅從10MB降低到不足1MB。

因為除了模塊聯邦帶來的直接收益,我們還享受到了它的附加收益,由於模塊聯邦本身引用的是一個組件,所以利用vue組件本身的特性,多個工單詳情我們可以複用同一個實例!

3、客服反饋

上線之後,我們再也沒有收到相關的任何負面反饋,例如打開工單詳情慢,或者説瀏覽器崩潰的反饋沒有了!

六、後續規劃

1、模塊聯邦的缺點:維護共享庫

它並不是沒有任何缺點,在配置過程中我們可以看到需要配置share字段,即不同項目需要共享的三方庫,例如A項目使用vue,B項目也使用vue,那麼就需要將vue聲明出來,插件會將vue自動分割出來,這樣A,B項目可以共享使用了,在remote端和host端都需要聲明,聲明如下:

shared: { 
  vue: { 
    // remote端該字段為requiredVersion 
    version: '3.1.5' 
  }, 
  axios: { 
    version: '0.21.1' 
  }, 
  vuex: { 
    version: '4.0.0' 
  }, 
  'vue-router': { 
    version: '4.0.8' 
  } 
 } 

但是,這裏對開發同學有非常不友好的點:

  • 需要將組件共享的包都手動找出來
  • 共享有時是強制的,如果漏了某些包,可能會導致頁面報錯或崩潰,想象一下頁面同時有兩個vue
  • 同一個庫,有時需要維護在約定的版本範圍內,有時一個包太老或太新也會在加載時報錯

簡而言之,就是對共享包的維護成本很大!

2、解決思路

  • 需要建立日常及生產構建時hook
  • 然後自建服務監聽這些hook,對比三方庫的包差異及其版本差異, 並拋出相應警告
  • 引入巡檢,構建完成後對遠程模塊部分進行檢查,因為不是每個改動都會使測試迴歸,但每個改動都可能引起頁面崩潰。
  • 對插件進一步封裝,使開發者無需關心三方依賴的管理。

3、與去應用化assets發佈的衝突

  • 由於assets發佈天然需要引入版本號,因此每個迭代所有的資源的地址都會帶入版本號
  • 遠程組件引用需要按約定確定一個remote端的入口文件地址,該地址基本是固定的

所以,目前remote端項目還不能使用assets發佈,接下來一是可以用接口的方式代替,二是需要團隊之間協商處理這個問題。

4、更大方向的暢想

還記得“可以打造一個去中心化的應用集羣”那張圖嗎?組件的引用不僅僅出現在團隊內部,也可能出現在團隊間共享,例如創建工單這個模塊就會被情報系統這樣的團隊引用。

即各個團隊間,或團隊內部的組件或模塊互相引用的,目前主要依靠iframe解決的,都可以使用模塊聯邦解決。

而公司內部也可以有這麼個平台,集合模塊聯邦的團隊,業務模塊展示管理,共享依賴包的校驗,警告或自動修補等等功能,來實現業務級別的團隊內外高效協作。

擴展閲讀:

【1】https://juejin.cn/post/685656...

【2】https://github.com/umijs/qian...\

【3】https://webpack.docschina.org...

【4】https://juejin.cn/post/684490...

【5】https://juejin.cn/post/691149...

【6】https://github.com/vitejs/awe...

【7】https://github.com/vitejs/awe...

文/LIUZHOUCHANG

關注得物技術,做最潮技術人!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.