導(dǎo)讀:本文由梯度科技前端研發(fā)部高級開發(fā)工程師賀信撰寫,主要介紹如何基于前沿開源的前端技術(shù)方案實(shí)現(xiàn)微前端在大數(shù)據(jù)平臺中的應(yīng)用落地,并對所取得的應(yīng)用效果進(jìn)行剖析。主要包括以下幾個方面:
案例背景
微前端解決方案對比和決策
實(shí)例應(yīng)用剖析
無界框架源碼與原理
實(shí)踐總結(jié)
一、案例背景
根據(jù)公司產(chǎn)品某項(xiàng)需求,需要引入一個新應(yīng)用作為一個獨(dú)立模塊集成進(jìn)已有大型平臺。公司本身產(chǎn)品的前端技術(shù)棧與待引入的新應(yīng)用技術(shù)棧不同,分別采用的是vue和react,使用的UI框架也截然不同。新應(yīng)用可以獨(dú)立開發(fā)部署,但為了產(chǎn)品體驗(yàn)的一致性,需要將新應(yīng)用嵌入已有業(yè)務(wù)中,并通過少量的改造使產(chǎn)品功能融為一體。
1.0階段:采用外鏈跳轉(zhuǎn)的方式,點(diǎn)擊菜單打開新tab頁展示數(shù)據(jù)開發(fā)模塊功能
2.0階段:采用iframe內(nèi)嵌頁面的方式,點(diǎn)擊菜單在當(dāng)前頁面通過iframe加載url
3.0階段:采用wujie微前端方案進(jìn)行父子應(yīng)用改造,組件式加載子應(yīng)用方便快捷
4.0階段:利用微前端能力實(shí)現(xiàn)模塊拆分節(jié)約50%工作量,新增跨模塊功能不再受限于技術(shù)棧
在引入wujie微前端方案后,對大數(shù)據(jù)平臺產(chǎn)生了積極影響:
收益變現(xiàn)
主框架不限制接入應(yīng)用的技術(shù)棧,微應(yīng)用具備完全自主權(quán),做到新技術(shù)選型時“技術(shù)棧無關(guān)”。
微應(yīng)用倉庫獨(dú)立,部署完成后主框架刷新時自動完成同步更新,每個微應(yīng)用之間狀態(tài)隔離,運(yùn)行時狀態(tài)不共享,獨(dú)立運(yùn)行時。
在面對各種復(fù)雜場景時,對已經(jīng)存在的系統(tǒng)做全量的技術(shù)棧升級或重構(gòu)不現(xiàn)實(shí),而微前端是一種非常好的實(shí)施漸進(jìn)式重構(gòu)的手段和策略,能夠做到增量升級。
便捷的alive保活模式,可以確保在用戶經(jīng)常切換路由的場景下不丟失編輯狀態(tài),非常適合大數(shù)據(jù)平臺的離線開發(fā)、實(shí)時開發(fā)、算法開發(fā)、低代碼等業(yè)務(wù)場景。
持續(xù)增值
開啟preloadApp預(yù)加載后,能顯著減少白屏?xí)r間,結(jié)合?;钅J娇色@得極速的打開體驗(yàn)。
支持 vite 等 ESM 腳本運(yùn)行。
提供無感知的iframe降級方案,理論上兼容到IE9,配合插件系統(tǒng)能解決99%的兼容問題。
未來接入其他第三方應(yīng)用(如BI、3D)將成為剛需,可無負(fù)擔(dān)的擴(kuò)展子應(yīng)用。
二、微前端解決方案對比和決策
可能有人會問目前市面上主流的微前端方案有很多,為什么選擇wujie微前端方案呢?該如何對比和決策?在這之前先來了解一下什么是微前端:
什么是微前端?
微前端是多個團(tuán)隊(duì)通過獨(dú)立發(fā)布功能的方式來共同構(gòu)建現(xiàn)代化 web 應(yīng)用的技術(shù)手段及方法策略,英文名稱叫 micro-frontends。
2016年,首次提出“微前端”的概念,微服務(wù)這個被廣泛應(yīng)用于服務(wù)端的技術(shù)范式開始擴(kuò)展到前端領(lǐng)域。
趨勢:前端越豐富復(fù)雜,項(xiàng)目越龐大難以維護(hù)。
核心思想:獨(dú)立的團(tuán)隊(duì)負(fù)責(zé)特定的業(yè)務(wù)和開發(fā)獨(dú)立的功能模塊。
架構(gòu)對比
需要注意的是,這些技術(shù)并不是相互排斥的,實(shí)際應(yīng)用中可能會使用它們的組合來實(shí)現(xiàn)應(yīng)用程序的需求。例如,同構(gòu)應(yīng)用可以與JAMStack和微前端一起使用,以提高應(yīng)用程序的性能、可維護(hù)性和可擴(kuò)展性。
實(shí)踐經(jīng)驗(yàn)告訴我們,永遠(yuǎn)不要追求最好的架構(gòu),而要追求最不糟糕的架構(gòu)。真實(shí)世界里完美的架構(gòu)并不存在,架構(gòu)也沒有對錯之分,只有根據(jù)環(huán)境進(jìn)行利弊權(quán)衡后的最佳結(jié)果。
微前端決策
在選擇何種方案決策的時候 我們整理了一份決策要素清單:
參考這份清單可以全方位對比每個框架的優(yōu)缺點(diǎn),幫助選擇最適合的方案。我們選取了目前市面上最流行的幾種方案進(jìn)行技術(shù)調(diào)研:
阿里開源的 qiankun 方案,基于 single-spa
京東開源的 micro-app 方案,基于 webcomponent + qiankun sandbox
歡聚時代開源的 EMP 方案,基于 webpack5 module federation
字節(jié)跳動開源 Garfish 方案,這是一個集部署、框架、調(diào)試于一體的全套解決方案
騰訊開源的 Wujie 方案,基于 WebComponent 容器 + iframe 沙箱
詳細(xì)調(diào)研了各類方案的優(yōu)缺點(diǎn)之后,我們根據(jù)主客觀判斷給出了一個評分表:
最終經(jīng)過多方面的對比和分析,并且在基于產(chǎn)品開發(fā)周期短、使用和改造成本低的實(shí)際情況下,決定選擇wujie方案。
三、實(shí)例應(yīng)用剖析
這部分內(nèi)容主要介紹wujie的實(shí)施方案和細(xì)節(jié),對應(yīng)wujie使用以及具體開發(fā)過程中解決的問題和最佳實(shí)踐。
控制臺展示
從瀏覽器按F12調(diào)出開發(fā)者工具,可以查看大數(shù)據(jù)平臺實(shí)施微前端改造后的父子應(yīng)用前端代碼結(jié)構(gòu):
從元素窗口看到,引入wujie后html里面的實(shí)際結(jié)構(gòu),其中wujie-app就是wujie創(chuàng)建的webcomponent自定義組件,可以理解為當(dāng)前位置之前使用的iframe標(biāo)簽,現(xiàn)在把標(biāo)簽替換為了wujie-app,里面的內(nèi)容就是子應(yīng)用也就是引入的新模塊的內(nèi)容,外面的內(nèi)容就是父應(yīng)用也就是原來已有的部分。
另外可以看到該自定義組件里面只有dom元素和css樣式內(nèi)容,最關(guān)鍵的js代碼去哪里了呢?繼續(xù)往下翻閱代碼到底部,發(fā)現(xiàn)在最底下多了一個iframe標(biāo)簽,這個標(biāo)簽的路徑是當(dāng)前網(wǎng)頁的路徑,樣式是隱藏的狀態(tài)。
子應(yīng)用的js是通過一個iframe沙箱的形式加載進(jìn)來的。在wujie微前端框架的影響下,子應(yīng)用的js和dom元素以及css樣式是分開的。
wujie使用
wujie的組件式加載方式非常方便快捷,本部門將介紹wujie的vue組件的使用方式。
wujie基于vue2、vue3、React框架的組件封裝了對應(yīng)的npm包:wujie-vue2、wujie-vue3、wujie-react
父應(yīng)用安裝并全局引入wujie-vue2。
常規(guī)的使用組件方式和props傳遞參數(shù),與普通組件別無二致。
子應(yīng)用本身無需安裝任何包,無界對子應(yīng)用注入了$wujie對象,可以通過多種方式獲取。
也就是說在滿足跨域條件下子應(yīng)用可以不用做任何改造,可以直接把iframe替換為wujie組件。
需要特別注意一點(diǎn)的是wujie的運(yùn)行模式,子應(yīng)用是否開啟了?;钜约笆欠襁M(jìn)行生命周期的改造會進(jìn)入完全不同的處理流程。
實(shí)際應(yīng)用方案
以下是我們根據(jù)項(xiàng)目實(shí)際情況使用的實(shí)施方案和原因理由,需要說明的是在不同公司不同項(xiàng)目是完全可以根據(jù)需求和wujie提供的各種能力來靈活調(diào)整方案的,這里并沒有什么最佳實(shí)踐,個人認(rèn)為只要能很好的滿足需求就都是最佳實(shí)踐。
保活模式
選用?;钅J降脑蛑饕幸韵聨讉€方面:
子應(yīng)用模塊代碼加載量大,?;钅J街恍枰淮渭虞d,避免頻繁切換白屏。
用戶在子應(yīng)用中使用monaco編輯器編輯內(nèi)容時容易忘記保存,用戶不希望退出當(dāng)前頁面就丟掉了未保存的輸入內(nèi)容。
子應(yīng)用模塊里面的底座是用了另外一個高度封裝的Web IDE UI框架,生命周期改造需要修改底座源碼,改造起來需要投入大量的時間成本。
props通信和EventBus方式結(jié)合
我們需要用到子應(yīng)用模塊里面不止一個頁面,且某幾個頁面要獨(dú)立出來,方便后續(xù)做權(quán)限控制。
通過location.query參數(shù)不同判斷展示
通過props傳遞模塊類型和jump方法
通過bus.$on和bus.$emit監(jiān)聽和觸發(fā)子應(yīng)用的路由變化
兩個wujieVue組件
3.0階段后需要將子應(yīng)用模塊拆分成兩個模塊,分別有兩個入口,所以用兩個組件來區(qū)分。
父應(yīng)用有兩個wujieVue組件,每個組件對應(yīng)多個頁面,props傳不同參數(shù)。
子應(yīng)用只需要一份代碼,巧妙的通過參數(shù)區(qū)分接口層、業(yè)務(wù)邏輯層,并在內(nèi)部發(fā)生路由跳轉(zhuǎn)時通知父應(yīng)用處理,節(jié)約了50%以上的開發(fā)時間和人力成本。
服務(wù)代理
這是父子應(yīng)用獨(dú)立部署兩個服務(wù)的情況圖,父子應(yīng)用之間用nginx做路由跳轉(zhuǎn)和接口代理
頁面結(jié)構(gòu)
在頁面結(jié)構(gòu)示意圖中可以看到綠色部分即原有平臺頁面,菜單導(dǎo)航還在原有代碼里,而新增的藍(lán)色部分就是用wujie包裹的部分。
數(shù)據(jù)流程
這是我們的路由和參數(shù)傳遞流程,可以看到我們是用props和EventBus路由同步邏輯的。
為什么我們沒有采用官方提供的路由同步功能,而是手動實(shí)現(xiàn)了一套路由同步邏輯呢?主要原因是由于子應(yīng)用(第三方模塊)不方便進(jìn)行生命周期改造。
根據(jù)官方文檔介紹,只有無界實(shí)例在初次實(shí)例化的時候才會從url上讀回路由信息,一旦實(shí)例化完成后續(xù)只會單向的將子應(yīng)用路由同步到主應(yīng)用url上。但在開啟路由同步后,刷新瀏覽器或者將url分享出去子應(yīng)用的路由狀態(tài)都不會丟失,會導(dǎo)致子應(yīng)用永遠(yuǎn)也不會刷新。在實(shí)際應(yīng)用過程中我們的多個頁面要通過query參數(shù)來區(qū)分不同頁面,因此必須通過更新路由狀態(tài)來切換不同的頁面。
四、wujie源碼與原理
接下來我們更深入的研究一下wujie的源碼和原理,對如何更好的使用微前端大有裨益。
wujie-core代碼結(jié)構(gòu)
可以看到wujie的核心代碼也十分簡單,總共14個文件,入口在index.ts,可以順著入口一點(diǎn)一點(diǎn)深入源碼進(jìn)行了解。
wujie-core核心代碼思維導(dǎo)圖
js沙箱和css沙箱鏈接原理和細(xì)節(jié)
子應(yīng)用的實(shí)例instance在iframe內(nèi)運(yùn)行,dom在主應(yīng)用容器下的webcomponent內(nèi)
無界在底層采用proxy + Object.defineproperty的方式將 js-iframe 中對 dom 操作劫持代理到webcomponent shadowRoot容器中,可以實(shí)現(xiàn)兩者的互聯(lián),開發(fā)者無感知也無需關(guān)心。
細(xì)節(jié):document的查詢類接口:getElementsByTagName、getElementsByClassName、getElementsByName、getElementById、querySelector、querySelectorAll、head、body全部代理到webcomponent,這樣instance和webcomponent就精準(zhǔn)的鏈接起來。
當(dāng)子應(yīng)用發(fā)生切換,iframe保留下來,子應(yīng)用的容器可能銷毀,但webcomponent依然可以選擇保留,這樣等應(yīng)用切換回來將webcomponent再掛載回容器上,這樣子應(yīng)用就可以獲得類似vue的keep-alive的能力。
webcomponent和proxy的降級方案
在非降級場景下,子應(yīng)用的dom在webcomponent中,運(yùn)行環(huán)境在iframe中,iframe對dom的操作通過proxy來代理到webcomponent上。
而webcomponent和proxy IE都無法支持,wujie采用另一個的iframe替換webcomponent,用Object.defineProperty替換proxy來做代理的方案。
降級的行為由框架判斷,當(dāng)瀏覽器不支持時自動降級。
降級后,應(yīng)用之間也保證了絕對的隔離度。
代碼無需做任何改動,之前的預(yù)加載、?;钸€有通信的代碼都生效,用戶不需要為了降級做額外的代碼改動導(dǎo)致降級前后運(yùn)行的代碼不一致。
五、實(shí)踐總結(jié)
常見問題
(1)跨域問題和cors設(shè)置
可能的原因分析:
子應(yīng)用的資源和fetch接口的請求都在主域名發(fā)起,所以會有跨域問題,子應(yīng)用必須做 cors 設(shè)置。
資源或接口請求沒有攜帶 cookie子應(yīng)用本身是用fetch發(fā)起請求,需要將子應(yīng)用fetch的credentials設(shè)置為include,這樣cookie才會攜帶上去。
或者在主應(yīng)用自定義fetch并將fetch的credentials設(shè)置為include。
(2)子應(yīng)用彈框位置不正確
冒泡系列組件(比如下拉框)彈出位置不正確。
解決方案:子應(yīng)用將body設(shè)置為 position: relative 即可。
子應(yīng)用彈窗根據(jù)點(diǎn)擊事件的 event.clientY 來確定top位置,但是主應(yīng)用頭部有導(dǎo)航欄導(dǎo)致位置計(jì)算不準(zhǔn)確。
解決方案:子應(yīng)用彈窗dom元素添加 position: fixed 樣式即可。
社區(qū)優(yōu)秀插件
wujie-polyfill
由于wujie(無界)采用的是WebComponents + iframe 來是腳本沙箱和樣式隔離,該倉庫用于彌補(bǔ)該方案的在特定的景下的不足。
插件列表:
LocationReloadPlugin (頁面刷新插件)
EventTargetPlugin (事件目標(biāo)插件)
WindowGetterPlugin (window獲取插件)
WindowMessagePlugin (window通信插件)
DocFullScrollPlugin (全屏插件)
InstanceofPlugin (原型鏈判定插件)
基本上目前常見的子應(yīng)用各種問題和坑都能在社區(qū)找到解決方案。
以上,我們介紹了微前端在大數(shù)據(jù)產(chǎn)品中的應(yīng)用背景、應(yīng)用理由、應(yīng)用方式和應(yīng)用原理,當(dāng)然我們在實(shí)踐過程中也不是一帆風(fēng)順的,中間也踩過不少坑走過不少彎路,也遇到過一些非常棘手一時無法解決的問題,所幸還有很多社區(qū)伙伴給予了很大的支持和幫助,梯度科技也將積極參與開源社區(qū)建設(shè)工作,回饋開源社區(qū),為開源社區(qū)持續(xù)提供優(yōu)質(zhì)的代碼與獨(dú)立的開源項(xiàng)目。
責(zé)任編輯:彭菁
-
模塊
+關(guān)注
關(guān)注
7文章
2655瀏覽量
47293 -
前端
+關(guān)注
關(guān)注
1文章
190瀏覽量
17709 -
代碼
+關(guān)注
關(guān)注
30文章
4722瀏覽量
68234 -
大數(shù)據(jù)
+關(guān)注
關(guān)注
64文章
8854瀏覽量
137212 -
服務(wù)端
+關(guān)注
關(guān)注
0文章
66瀏覽量
6978
原文標(biāo)題:微前端在大數(shù)據(jù)平臺中實(shí)際應(yīng)用案例剖析
文章出處:【微信號:gh_681e57b24d17,微信公眾號:梯度科技】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論