<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于wujie的解決方案來簡單聊聊微前端

          共 14074字,需瀏覽 29分鐘

           ·

          2024-06-28 08:30

          點擊關(guān)注公眾號,“技術(shù)干貨” 及時達(dá)!

          「1.5W 字預(yù)警」

          前言

          因為目前有時間了,所以在整理一下自己這幾年寫過的一些東西的相關(guān)文檔,準(zhǔn)備把一些東西改一下發(fā)出來,有的內(nèi)容可能并不復(fù)雜,甚至有點淺顯,但是也是對自己這幾年的一些復(fù)盤和總結(jié)了.

          往期精彩

          什么是微前端,它解決了什么問題

          什么是微前端

          微前端這個概念相信對于前端來講,其實并不陌生,大家也或多或少看到過相關(guān)的文章、或者有過相關(guān)的實踐,如果我們?nèi)ニ阉鳎菏裁词俏⑶岸?/p>

          那么我們其實很容易找到以下一些類似的定義:

          1. 是將前端應(yīng)?分解成?些更?、更簡單的能夠獨?開發(fā)、測試、部署的?塊,?在?戶看來仍 然是內(nèi)聚的單個產(chǎn)品的技術(shù)或思想。
          2. 將某個單?的單體應(yīng)?,轉(zhuǎn)化為多個可以獨?運?、獨?開發(fā)、獨?部署、獨?維護的服務(wù)或者應(yīng)? 的聚合,從?滿?業(yè)務(wù)快速變化及分布式多團隊并?開發(fā)的需求
          3. .......

          等等,諸如此類的定義

          「這時候可能會有同學(xué)會說:哎嘿?那我搞幾個項目里面套幾個iframe那不也是微前端?」

          沒錯,如果按照上面的微前端的定義來說,iframe或許就是最初的微前端方案了,甚至連通過nginx路由轉(zhuǎn)發(fā)來組合不同項目的功能組成一個系統(tǒng)都能可以是微前端。

          那這時候可能不太清楚的同學(xué)就會想:既然基于 iframe 我們就可以搭建一套微前端的系統(tǒng)了,那為什么現(xiàn)在業(yè)界的微前端方案還層出不窮,各自都給出了自己的答卷?

          關(guān)于這個問題,莫急,關(guān)于這個我們稍后簡單討論一下

          「前面說了這么多,那微前端到底是什么呢?」

          就像我們前面說過的一樣,我們將不同的功能模塊或業(yè)務(wù)通過例如
          1、按照業(yè)務(wù)
          2、按照權(quán)限
          3、按照變更的頻率
          4、按照組織結(jié)構(gòu)
          5、跟隨后端微服務(wù)設(shè)計
          6、從代碼出發(fā)的ddd實踐
          (可能很多人了解 ddd 這個概念都是在微前端或者微服務(wù)入坑的,手動滑稽.jpg)

          等等的各種適用于不同實際業(yè)務(wù)場景的原則來劃分出不同的子應(yīng)用,通過「組合」的方式來組成一個完整應(yīng)用的思想和技術(shù)方案 (所以微前端的拆分通常沒辦法簡單抄作業(yè))

          所以,微前端本質(zhì)上是一種通過 「模塊化、拼圖式」的開發(fā),以「組合的思想」來降低前端集成的復(fù)雜度和成本的思想(以上概念僅限于個人理解,本人不為該觀點正確性負(fù)責(zé))

          說到組合的思想,這時候可能很多同學(xué)都會不約而同地想到:

          「嘿,終于到我熟悉的領(lǐng)域了」

          在目前組合代替繼承的思想流行下,相信每位前端同學(xué)對組合的思想都有自己的經(jīng)驗和見解,那么在這種思想下進行的前端開發(fā)中碰到的一些組合的思想所帶來的問題,其實在微前端這種應(yīng)用層面的組合上也不能逃脫

          具體的內(nèi)容我們會在第二部分中再進行簡單的討論

          微前端適用于什么場景

          在前面我們拋出了一大堆的概念,大家其實對微前端的思想也有了一定的了解,很多人可能這時候會在想:
          前面巴巴巴說了一大堆,那我一定就要用到這玩意嗎?
          它能給我們的業(yè)務(wù)帶來什么價值,能超過它所帶來的項目管理的問題嗎?
          (雖然我們可以通過 Lerna+Monorepo之類的工程結(jié)構(gòu)來緩解管理的問題)
          我們面臨的問題就非它不可,必須要從架構(gòu)的層面上去改變嗎?

          其實在采用微前端架構(gòu)的時候,不管是用 qiankun,還是用 iframe,抑或是其他的什么解決方案,能用不同框架只是添頭,實際上并沒有觸及到問題的本質(zhì),微前端各個部分之間相互獨立,獨立部署的能力本質(zhì)上是在允許構(gòu)建孤立或「松散耦合」的服務(wù)。

          而松散耦合的系統(tǒng),對于開發(fā)、維護、還是后期漸進式重構(gòu)的好處都是毋庸置疑的。

          什么是松散耦合

          松散耦合是各種相互聯(lián)合事件的反映但是,每一個事件也都在保持自身的獨特性,也存在著某些物質(zhì)或邏輯上的分離,各種結(jié)構(gòu)性要素松散聯(lián)系,但是結(jié)構(gòu)對結(jié)果基本沒有什么影響的狀態(tài),有興趣的同學(xué)可以了解一下相關(guān)概念,挺有意思的

          所以在下面我們可以簡單討論一下,它更具體一點的應(yīng)用場景

          1. 治理巨石應(yīng)用

          image.png

          這個無疑是提起微前端時最容易被聯(lián)想到的場景,在實際的開發(fā)生涯中其實我們經(jīng)常能碰到以下的經(jīng)典場景:

          「(1) 在進入一個新團隊的時候,經(jīng)常有可能接手到一個 5 年陳的項?,或者我們需要重啟一個多年未維護的項目」

          這個項目可能會有強?混?多種技術(shù)棧的情況,例如我們現(xiàn)在這個六年陳的pc端就會有react 和 jq 混合開發(fā)的情況,或者是使?的技術(shù)棧落后、較為?眾或?qū)W習(xí)成本過?。

          例如Foundation、angular1.x、Easy Framework之類的玩意,又或者是重構(gòu)不徹底的代碼,經(jīng)歷了了重構(gòu)-爛尾-又重構(gòu)-又爛尾的項目(沒錯,說的上面那項目)

          那這時候我們把原有功能抽離為單獨的子應(yīng)用,而新的功能模塊作為新的子應(yīng)用嵌入的辦法來保證在逐漸重構(gòu)的同時既要保證中間版本能夠平滑過渡,同時持續(xù)交付新的功能?(只是一個思路,不一定就是用這種方式)

          「(2) 保證當(dāng)前技術(shù)?案在 3-5 年的業(yè)務(wù)迭代后還保有?命?,不會變成?個遺產(chǎn)項?」

          「(3) 避免代碼庫不斷膨脹?帶來的各種問題」

          「例如」

          1. 因為單體應(yīng)用的不斷膨大導(dǎo)致的理解和修改成本的不斷上升

          2. 從代碼的提交到實際部署的周期越來越?,并且很容易出問題,例如我們的H5項目有接近一百五十個打包入口,在每一個版本的迭代都需要重新全量打包部署,之前在有集團內(nèi)項目打包時間統(tǒng)計的時候也名列打包最長時間的前三

          3. 難以交付可靠的單體應(yīng)?, 系統(tǒng)龐?復(fù)雜 -> ?法進?全??徹底的測試 -> 代碼中的錯誤會進??產(chǎn)環(huán)境 -> 因為程序中的代碼都在同?進程中運?,應(yīng)?缺乏故障隔離 -> 可能出現(xiàn)?些例如:內(nèi)存泄漏 之類的問題導(dǎo)致?戶運?過久后崩潰之類的問題

            例如node寫的中間件的實例則有可能崩潰導(dǎo)致?半夜因為?產(chǎn)環(huán)境的問題爬起來查bug

          4. 需要?期依賴某個可能已經(jīng)過時的技術(shù)棧,并且框架難以升級新的版

          等等。。。。。。。。

          2.快速驗證

          其實我們碰到

          「(1)產(chǎn)品想要去上線一個試驗性的活動或者功能的場景非常多」

          這類功能在用戶反饋不好的時候或許在很短的時間內(nèi)就會被下線,而這個活動或功能的上線和下線每次都要經(jīng)歷一個成本較高的過程。

          「(2)同時驗證同一個功能的不同實現(xiàn)」

          而在這方面其實就像酷狗曾經(jīng)有一個組件即服務(wù)的微前端實現(xiàn),每個直播間的組件靈活控制上下線,對于用戶反饋的時機把握賊靈活

          3. 應(yīng)?功能?由組合拆分及定制化開發(fā)

          例如我們的目前的業(yè)務(wù)、實際上經(jīng)常有可能會面臨對不同學(xué)校有不同的特定的定制化需求或者功能組合,那實際上我們可以對每個單獨的功能作為一個單獨的子系統(tǒng)開發(fā)、?系統(tǒng)間的耦合只需要規(guī)定好相應(yīng)的通訊?式和內(nèi)容,不需要關(guān)注對?的實現(xiàn),在需要的時候自由組合即可。

          4. 可同時灰度多條產(chǎn)品功能等等

          因為松散耦合的系統(tǒng)結(jié)構(gòu)可應(yīng)用的場景太多了,所以就不一一列舉了

          如果我們需要微前端的能力我們需要關(guān)注什么

          如果我們需要一個微前端方案,我們需要關(guān)注什么?

          通過第一部分的簡單描述,我們大概了解了什么是微前端,那么,在我們假設(shè)對目前流行的微前端的技術(shù)方案或者架構(gòu)都不清楚的情況下,倘若我們需要去實踐這么一個微前端的架構(gòu),我們需要關(guān)注哪些切實的問題?

          組件之間的組合最重要的是什么

          跟我們在第一部分所提到的一樣,微前端的實現(xiàn)本質(zhì)上也是一種組合的思想,子應(yīng)用間的組合其實和功能中組件的組合有一定程度上的異曲同工之妙,那么我們組件之間的組合最重要的是什么?

          沒錯!就是 「通訊和狀態(tài)管理」

          就好像組件一樣,子應(yīng)用總會有各樣的組合方式,那么父子應(yīng)用間的通訊、兄弟應(yīng)用之間的通訊、甚至爺孫應(yīng)用之間的通訊就成了一個需要關(guān)注的問題。

          既然是由不同的子應(yīng)用之間組合而成,無法避免狀態(tài)管理的問題,無論是全局下的狀態(tài)管理、幾個子應(yīng)用間的局部狀態(tài)管理,還是單個子應(yīng)用間的狀態(tài)管理,以及狀態(tài)的上傳和下發(fā),都將是我們需要去考慮的問題

          我們簡單舉一個例子,對于數(shù)據(jù)和狀態(tài)的管理、在你想采用微前端架構(gòu)的時候,不管是用 qiankun,還是用 iframe,用不同框架只是添頭,沒什么作用(因為編程語言或者目標(biāo)語言沒有改變),很多時候你遇到的問題可以轉(zhuǎn)化成這樣

          就容易變成了redux 動機文檔中的曼妥思糖 如果一個 model 的變化會引起另一個 model 變化,那么當(dāng) view 變化時,就可能引起對應(yīng) model 以及另一個 model 的變化,依次地,可能會引起另一個 view 的變化。直至你搞不清楚到底發(fā)生了什么

          單獨看這個問題,你會很想當(dāng)然地說出狀態(tài)提升的解法

          這種解法沒有問題,但是,當(dāng)你這么解決問題,一旦應(yīng)用范圍內(nèi)廣泛存在數(shù)據(jù)耦合,你怎么辦?

          這時候必然將所有數(shù)據(jù)放置于全局單例,雖然犧牲了多例和初始化控制以及析構(gòu)控制能力,但是確實能大大減少開發(fā)負(fù)擔(dān),這種解法就是 redux 或者說狀態(tài)管理的本質(zhì)

          但是!

          這種解法在微前端中是完全無法使用的

          因為微前端架構(gòu),在應(yīng)用這一層級之上,目標(biāo)是「分開開發(fā),分開構(gòu)建,分開部署」

          「你能將單一數(shù)據(jù)原則應(yīng)用于此么?」

          答案是不能的,每個應(yīng)用有自己的 store,最終還是會回到第一個例子的問題上去

          所以,我們要正視怎么解決這樣的問題,這才是微前端之所以微,松散耦合之所以松散的本質(zhì)

          「其實這個本質(zhì)上是兩個問題」

          1、子會響應(yīng)父的狀態(tài)變化,父會在子初始化之后初始化,子會在父變更后變更,導(dǎo)致子狀態(tài)必須在子組件內(nèi)部
          2、react 以及狀態(tài)管理,只有子向父 dispatch 事件的能力,父無法向子 dispatch 事件

          即使是換到父子應(yīng)用層面上的理解也依舊如此

          而且我們需要知道一件事,在第一部分中其實我們有提過微前端的應(yīng)用間的關(guān)系其實也是一種組合關(guān)系

          在組合關(guān)系中,「被組合對象是絕對會耦合于源對象的」

          所以我們在狀態(tài)管理的層面上將組合的方式轉(zhuǎn)為更松散的邏輯關(guān)系,例如:聚合?

          而這種轉(zhuǎn)換其實一般來說我們都是通過「依賴注入」的方式去實現(xiàn)的

          但是這種情況下,子組件依然無法擁有自己的狀態(tài),因為這依然是單一數(shù)據(jù)處理方式,只不過沒有隔一層 props

          并且這時候還是沒能解決 只有子向父 dispatch 事件的能力,父無法向子 dispatch 事件的問題

          「那我們需要怎么解決這個問題?」

          其實這個問題很好解決,實現(xiàn)一個「發(fā)布訂閱模型」就可以了,所以這也是為什么qiankun之類的微前端方案的應(yīng)用間通訊方式是使用發(fā)布訂閱的方式進行

          這樣處理,parent 和 Child 就可以彼此通過事件傳遞消息,且獨立變化,讓整個結(jié)構(gòu)松散耦合起來

          工作空間的獨立

          js沙箱相關(guān):https://zhuanlan.zhihu.com/p/527437146

          「js 沙箱」

          因為我們每一個子應(yīng)用都是一個單獨的項目和應(yīng)用,在同一個頁面中出現(xiàn)多個子應(yīng)用是很常見的場景

          那么出于安全的考慮,例如全局變量污染、多版本庫、以及上面提到的故障隔離,還有各種復(fù)雜的場景下的執(zhí)行問題

          我們理所當(dāng)然是需要對每個子應(yīng)用間的 js 的工作空間進行隔離,使每個子應(yīng)用內(nèi)的執(zhí)行自洽,只關(guān)注輸出和輸入

          那么js沙箱的實現(xiàn)也就無疑成為了微前端方案中急需關(guān)注的一點。

          「css 沙箱」

          既然關(guān)注了js的隔離,那css的隔離自然也逃不掉

          雖然是微前端的結(jié)構(gòu),但是本質(zhì)上同一個頁面中每個子應(yīng)用的掛載依舊在同一個dom樹上

          那么我們自然不希望子應(yīng)用間的樣式會出現(xiàn)互相干擾的情況,并且在子應(yīng)用切換時可以自行裝載和卸載。


          說句實話,單純的iframe實現(xiàn)微前端的方案雖然有很多問題,但是在js和css的隔離的上無疑是成本最低且較好的實現(xiàn)

          預(yù)加載

          因為在微前端的方案中,每個子應(yīng)用都是獨立的,所以如果不做任何處理的話在一個頁面存在多個子應(yīng)用的情況下,每個子應(yīng)用的加載都是獨立的,在這種情況下我們就很容易讓用戶體驗到不同功能之間陸續(xù)從白屏到加載完成的割裂過程。這個無疑是致命的缺陷。

          并且在實際用戶使用中,瀏覽器大部分時間是處在空閑的,我們要怎么利用這樣的空閑時間,去加載其他子應(yīng)用的JS,用來優(yōu)化用戶體驗?

          公共依賴的處理問題

          這個問題屬于我們軟件開發(fā)中項目管理的部分,子項目多了之后,公共依賴如果處理不好,不但造成我們開發(fā)工時的浪費,有各種重復(fù)工作,同時BUG的風(fēng)險也會隨著復(fù)制的代碼過多,成指數(shù)增長,更不要說日后的長期維護

          1. 可以企業(yè)內(nèi)部搭建npm服務(wù)器發(fā)布到,但問題也很明顯,例如npm包更新,需要手動更新、任何改動都需要子應(yīng)用重新部署上線,用的子應(yīng)用多了,這更新氣來就麻煩死了
          2. webpack external 外部擴展,可以將通用的一些包排除在bundle之外,然后使用直接訪問公共包JS的方式(一般采用CDN),直接在index.html中引入,但是這樣的話,公共依賴必須采用UMD格式,那用的時候也要遵循,就有點麻煩
          3. webpack federation 模塊聯(lián)邦,可以使一個JS應(yīng)用,動態(tài)加載其他JS應(yīng)用的代碼,并且我們可以把一些公共依賴,都抽離到主包。在子包中,只輸出業(yè)務(wù)代碼即可,模塊聯(lián)邦提供對應(yīng)的配置功能,并且由于是從網(wǎng)絡(luò)獲取,可以做做熱更新。但是老項目要升級webpack,子項目和主項目都需要進行webpack federation的配置工作,才能用,這個也是要開發(fā)工作量,太麻煩
          4. monorepo 多包管理,目前主流使用lerna框架進行多包管理,把單獨的包抽離到獨立的子項目中維護,后期如果項目穩(wěn)定,可以把依賴抽離到webpack federation 做熱更新,但是也好麻煩

          路由的管理

          這個為啥要關(guān)注不用我多說了吧,這位靚仔,你也不想你的路由不知道咋跳對吧。目前我知道的有兩個方案

          路由劫持

          方案原理大致是監(jiān)聽了popstats或者h(yuǎn)ashchange事件,并劫持了瀏覽器history下的pushState和replaceState后

          主應(yīng)用控制路由

          實現(xiàn)思路是主應(yīng)用使用現(xiàn)有的路由庫,例如vue router或者react router,子應(yīng)用使用webpack federation,將現(xiàn)有的頁面發(fā)布成獨立的服務(wù),在主應(yīng)用中重新配置路由,使用webpack提供的import()函數(shù),動態(tài)加載子用的模塊

          但是上面其實沒有考慮到子應(yīng)用自身的路由跳轉(zhuǎn)、子應(yīng)用a跳轉(zhuǎn)到子應(yīng)用b,或者子應(yīng)用a要打開子應(yīng)用b的指定路由的跳轉(zhuǎn)之類的場景

          子應(yīng)用的資源加載方式

          是html entry還是js entry。別問,問就是快,一個加載的是按原來方式打包出來的一個html文件,一個是加載子應(yīng)用打出來的整個js文件。

          而且將整個微應(yīng)用打包成一個 JS 文件常見的打包優(yōu)化基本上都沒了,按需加載、首屏資源加載優(yōu)化、css 獨立打包等想都別想

          支不支持應(yīng)用保活

          事關(guān)keep-alive之類的需求在微前端的架構(gòu)中怎么做,這位靚仔你也不想切個子應(yīng)用,之前的狀態(tài)就全沒了吧

          子應(yīng)用之間的互相嵌套

          支不支持子應(yīng)用之間的互相嵌套,子應(yīng)用件互相嵌套情況下的通訊和狀態(tài)管理

          是否有生命周期的設(shè)計

          小結(jié)

          當(dāng)然,以上這些實際上只是具體在落地微前端方案的時候需要關(guān)注的具體問題 但是從更高一點的層面上來看

          其實我個人覺得主要其實要關(guān)注的有「三個緯度和兩個問題」

          三個維度:代碼的管理、工程的獨立性、優(yōu)雅地集成

          兩個問題:什么場景適用、怎么更好地拓展


          其實以上的問題都是圍繞著解決 安全性和域完整性這兩塊問題

          包括了請求安全、數(shù)據(jù)安全、配置安全、界面完整、交互完整、元素完整等,但是這里我們就不細(xì)細(xì)討論了

          畢竟這只是一個四十分鐘不到的分享


          「所以我們最后的小結(jié)是」

          在我們需要一個微前端方案時,我們需要考慮的問題有如下幾點:

          • 應(yīng)用間的通訊
          • 應(yīng)用間的狀態(tài)管理
          • js 沙箱
          • css 隔離
          • 預(yù)加載
          • 公共依賴的處理
          • 路由狀態(tài)管理
          • 是否支持html entry
          • 應(yīng)用支不支持保活
          • 子應(yīng)用之間的互相嵌套
          • 是否有生命周期的設(shè)計

          目前都有什么流行的技術(shù)方案,它們解決了什么問題

          通過第二部分的思考,我們大概了解了,假設(shè)我們在對現(xiàn)有的微前端解決方案都不清楚的情況下

          我們需要去落地一個微前端的架構(gòu),我們將會需要關(guān)注哪方面,以及需要應(yīng)對哪些可能會出現(xiàn)的問題

          那么在這一部分,我們將會去對比一下當(dāng)前除了 wujie 以外的較為流行的微前端方案

          簡單探討一下他們的實現(xiàn)方案的優(yōu)劣,以及未來可能會存在的發(fā)展方向

          (因為時間有限,并且不是此次分享重點,所以不會過于詳細(xì))

          nginx轉(zhuǎn)發(fā)

          根據(jù)我們第一部分對微前端的定義來說,nginx轉(zhuǎn)發(fā)來分割和組合應(yīng)用其實也能算是一種微前端的實現(xiàn)方案 但是根據(jù)我們第二部分的思考來說,顯然大部分問題的處理都是做不到的,所以這里就貼個圖出來湊個數(shù)算了

          純iframe的實現(xiàn)方案

          根據(jù)我們第二部分的思考,其實iframe的方案在一些地方有著天然的優(yōu)勢,例如 iframe 的隔離完美,無論是 js、css、dom 都完全隔離開來,子應(yīng)用間的嵌套毫無壓力,隨便套,并且使用簡單,沒有任何心智負(fù)擔(dān),天然支持html entry

          但是「缺點」也非常明顯:

          1. ?論是使?postMessage還是通過 iframeEl.contentWindow 去獲取 iFrame 元素的 Window 對象,?或者是直接???調(diào)?????法:FrameName.window.childMethod();???調(diào)?????法:parent.window.parentMethod();來通訊都并不是太過友好的事情,需要設(shè)計?套規(guī)范的通訊標(biāo)準(zhǔn),實在過于麻煩,并且狀態(tài)管理、公共依賴的處理等都能通過其他的方式封裝實現(xiàn)
          2. 路由狀態(tài)丟失,刷新一下,iframe 的 url 狀態(tài)就丟失了
          3. dom 割裂嚴(yán)重,彈窗只能在 iframe 內(nèi)部展示,無法覆蓋全局,并且事件傳遞上存在者很大的問題,例如拖拽
          4. 白屏?xí)r間太長,對于SPA 應(yīng)用應(yīng)用來說無法接受
          5. 難以做預(yù)加載
          6. 應(yīng)用完全沒辦法保活,每次都是新的加載
          7. iframe和主??共享連接池,?瀏覽器對相同域的連接有限制,所以會影響??的并?加載,出現(xiàn)iframe中的資源占?了可?連接?阻塞了主??的資源加載

          所以這很難說得上是一個比較好的實現(xiàn)方案,但是后續(xù)我們會講到wujie是怎么解決iframe的缺點,打造一個接近完美的iframe方案的。

          基座模式的代表 qiankun.js (基于single-spa)

          qiankun是一個很經(jīng)典的基座模式下的微服務(wù)方案,但是在使用成本上來說是有點大的,它對代碼的侵入型很強,如果要改造的話,從 webpack、代碼、路由等等都要做一系列的適配

          能力 現(xiàn)狀
          通訊與狀態(tài)管理 1、初始化狀態(tài)通過props傳入子組件
          2、qiankun通過initGlobalState, onGlobalStateChange, setGlobalState實現(xiàn)主應(yīng)?的全局狀態(tài)管理,然后默認(rèn)會通過props將通信?法傳遞給?應(yīng)?,但是本質(zhì)上還是通過發(fā)布 - 訂閱的方式來進行通訊
          3、主子應(yīng)用localStrage、cookie可共享 所以在通訊上并不是很完美
          js和css隔離 提供js和css隔離,但是在js的沙箱方面依然有不少坑有問題,近一年的很多changelog都是在修js沙箱的問題
          預(yù)加載 做了靜態(tài)資源的預(yù)加載能力
          公共依賴的處理 文檔內(nèi)寫明不推薦,但是硬要的話可以在微應(yīng)用中將公共依賴配置成 external,然后在主應(yīng)用中導(dǎo)入這些公共依賴的
          路由管理 1、每個子應(yīng)用中注冊,然后由主應(yīng)用進行管理,主應(yīng)用的路由實例通過 props 傳給微應(yīng)用,微應(yīng)用這個路由實例跳轉(zhuǎn)
          2、注冊微應(yīng)用的基礎(chǔ)配置信息。當(dāng)瀏覽器 url 發(fā)生變化時,會自動檢查每一個微應(yīng)用注冊的 activeRule 規(guī)則,符合規(guī)則的應(yīng)用將會被自動激活
          3、頁面上不能同時顯示多個依賴于路由的微應(yīng)用,因為瀏覽器只有一個 url,如果有多個依賴路由的微應(yīng)用同時被激活,那么必定會導(dǎo)致其中一個 404。
          「(因為基于路由匹配,所以無法同時激活多個子應(yīng)用)」
          html entry 支持
          應(yīng)用保活 無法支持子應(yīng)用保活
          應(yīng)用嵌套 支持
          生命周期 支持


          micro-app

          能力 現(xiàn)狀
          通訊與狀態(tài)管理 基于發(fā)布訂閱+CustomEvent
          js和css隔離 js: 使用Proxy攔截了用戶全局操作的行為。
          css: 利用標(biāo)簽的name屬性為每個樣式添加前綴,將子應(yīng)用的樣式影響禁錮在當(dāng)前標(biāo)簽區(qū)域,但是依舊沒辦法必定隔絕
          預(yù)加載 在瀏覽器空閑時間,依照開發(fā)者傳入的順序,依次加載每個應(yīng)用的靜態(tài)資源,以確保不會影響基座應(yīng)用的性能
          公共依賴的處理 沒有解決基座應(yīng)用和子應(yīng)用共用依賴的特性,issues中回答了在計劃中,但是還沒想好怎么做
          路由管理 每個應(yīng)用的路由實例都是不同的,應(yīng)用的路由實例只能控制自身,無法影響其它應(yīng)用,包括基座應(yīng)用無法通過控制自身路由影響到子應(yīng),路由跳轉(zhuǎn)只有三種方式:window.history,通過history.pushState或history.replaceState進行跳轉(zhuǎn)、通過數(shù)據(jù)通信控制跳轉(zhuǎn)(基座控制子應(yīng)用跳轉(zhuǎn),子應(yīng)用監(jiān)聽基座數(shù)據(jù)變化而跳轉(zhuǎn))、傳遞路由實例方法,把實例傳到子應(yīng)用里去跳轉(zhuǎn)(子應(yīng)用控制基座跳轉(zhuǎn))
          url屬性和子應(yīng)用路由沒有關(guān)系,只是用來加載html資源
          html entry 支持, 實際上是以類WebComponent + HTML Entry實現(xiàn)微前端的組件化,但是webcomponents沒有做降級處理
          應(yīng)用保活 支持,< micro-app name='xx' url='xx' keep-alive>
          應(yīng)用嵌套 支持
          生命周期 支持


          Module Federation

          image.png
          能力 現(xiàn)狀
          通訊與狀態(tài)管理 別想了,互相之間都是獨立模塊,并且去中心了,你還想咋共享?
          js和css隔離 js: 沒有沙箱,全憑開發(fā)者自覺不瞎搞
          css: 別想了,只能通過 postcss-selector-namespace 添加前綴或者別名之類的方式來處理 簡單來說,沒有有用的 css 沙箱和 js 沙箱,一切需求靠用戶自覺
          預(yù)加載 沒有利用瀏覽器空閑時間去做子應(yīng)用加載的處理
          公共依賴的處理 天然支持
          路由管理 微應(yīng)用的路由是有發(fā)生沖突的可能性的,為了實現(xiàn)獨立部署能切換頁面,各微應(yīng)用都有自己的路由,要解決就只能微應(yīng)用單獨部署時,使用 web 路由,集成到 container 時,使用內(nèi)存路由,web 路由由 container 接管,瀏覽器地址欄變化時,告 訴集成進來的微應(yīng)用,然后微應(yīng)用再跳轉(zhuǎn)到相應(yīng)的頁面。
          html entry 沒有
          應(yīng)用保活 沒有,別想
          應(yīng)用嵌套 想怎么套就怎么套
          生命周期 別想


          wujie 是怎么解決以上問題的?

          通過第二和第三部分的思考,我們大概了解了落地一個微前端的架構(gòu)可能會需要面臨的挑戰(zhàn),以及當(dāng)前流行的技術(shù)方案對于面臨的問題提出了哪些思想或解決方案 所以第四部分我們就簡單講講第二部分我們所考慮到的問題在 wujie 的方案中是怎么處理的

          其實我們可以直接看 wujie 的github倉庫 可以發(fā)現(xiàn)它的實現(xiàn)實際上非常簡單,加起來不過3000行代碼,所以打消了我想挑一塊源碼功能的實現(xiàn)來講用于消磨時間的打算(滑稽.jpg)


          wujie是怎么解決純 iframe 微前端方案所遇到問題的?

          wujie 是一個iframe + webComponent 的解決方案 既然提到了iframe,那么我們肯定要關(guān)注前面提到的iframe存在著通訊、路由、dom割裂嚴(yán)重、白屏?xí)r間長、難做預(yù)加載、應(yīng)用無法保活、瀏覽器對相同域的連接有限制,會影響??的并?加載等問題在wujie中是否被解決

          最重要的通訊問題

          在wujie中提供了三種通訊方式:
          (1 「props 通信」,主應(yīng)用可以通過props注入數(shù)據(jù)和方法
          (2 「window 通信」,由于在設(shè)計上子應(yīng)用運行的iframe的src和主應(yīng)用是同域的,所以相互可以直接通信,這個我們在下一步中細(xì)述
          (3 「eventBus 通信」 其中最有趣的就是這一點,wujie 提供了一套去中心化的通訊方式


          其中的 eventBus 的功能實現(xiàn)非常簡單簡潔,整塊功能的實現(xiàn)不過 105 行,除了全部事件的存儲的map會根據(jù)是否存在wujie進行判斷和兼容之外,本質(zhì)上就是一個非常常規(guī)的發(fā)布訂閱的實現(xiàn),跟咱們今年校招的筆試題其實是一樣的,撐死是個加強版

          iframe 問題解決:dom 割裂嚴(yán)重、路由狀態(tài)丟失、通信非常困難的問題

          路由狀態(tài)的問題

          假如我們在應(yīng)用a中想要加載應(yīng)用b,那么我們可以怎么做才能避免上述問題?

          在應(yīng)用 A 中構(gòu)造一個shadowRoot 和iframe,然后將應(yīng)用 B 的html寫入shadowRoot中,js運行在iframe中,因為iframe的js隔離真的很完美。

          這時候我們注意 iframe 的 url,iframe保持和主應(yīng)用同域但是保留子應(yīng)用的路徑信息,這樣子應(yīng)用的js可以運行在iframe的location和history中保持路由正確。

          「這樣就可以解決了路由狀態(tài)的問題,并且解決可以使用window來通訊」

          Shadow DOM API 的 ShadowRoot 接口是一個 DOM 子樹的根節(jié)點,它與文檔的主 DOM 樹分開渲染。

          「源碼」


          dom割裂的問題

          那在iframe中dom割裂的問題要怎么處理?

          在iframe中攔截document對象,統(tǒng)一將dom指向shadowRoot,此時比如新建元素、彈窗或者冒泡組件就可以正常約束在shadowRoot內(nèi)部。

          我們可以從源碼中看出,在非降級的情況下, wujie對iframe的 document進行了代理,從而解決了解決了 dom 割裂嚴(yán)重的問題


          首次白屏的問題

          那這時候?qū)嶋H就只剩下了白屏、預(yù)加載和應(yīng)用保活的問題

          白屏實際上可以分為兩個場景,一是「首次加載白屏」,二是「切換應(yīng)用時白屏」

          「首次白屏的問題」,wujie實例可以提前實例化,包括shadowRoot、iframe的創(chuàng)建、js的執(zhí)行,這樣極大的加快子應(yīng)用第一次打開的時間

          「切換白屏的問題」,一旦wujie實例可以緩存下來,子應(yīng)用的切換成本變的極低,如果采用保活模式,那么相當(dāng)于shadowRoot的插拔

          所以以上的整套機制在wujie中的實現(xiàn)對應(yīng)的流程圖如下圖一樣


          預(yù)加載的處理

          對于預(yù)加載的處理也非常簡單,在子應(yīng)用使用fiber模式執(zhí)行的情況下,使用 requestIdleCallback ,在瀏覽器空閑時間去加載資源

          js和css隔離

          這個不用多言,懂的都懂

          公共依賴的處理

          文檔內(nèi)寫明不推薦,但是硬要的話可以在微應(yīng)用中將公共依賴配置成 external,然后在主應(yīng)用中導(dǎo)入這些公共依賴的

          路由管理

          跟其他方案不同的是,wujie的路由管理有兩個比較有意思的點

          「1、路由同步」

          會將子應(yīng)用路徑的path+query+hash通過window.encodeURIComponent編碼后掛載在主應(yīng)用url的查詢參數(shù)上,其中key值為子應(yīng)用的 name。

          開啟路由同步后,「刷新瀏覽器或者將url分享出去子應(yīng)用的路由狀態(tài)都不會丟失」,當(dāng)一個頁面存在多個子應(yīng)用時無界支持所有子應(yīng)用路由同步,瀏覽器刷新、前進、后退子應(yīng)用路由狀態(tài)也都不會丟失,并且「提供短路徑的能力」,當(dāng)子應(yīng)用的url過長時,可以通過配置 prefix 來縮短子應(yīng)用同步到主應(yīng)用的路徑,無界在選取短路徑的時候,按照匹配最長路徑原則選取短路徑。

          「2、路由跳轉(zhuǎn)」

          無界支持子應(yīng)用間的路由的跳轉(zhuǎn),例如子應(yīng)用a可以跳轉(zhuǎn)子應(yīng)用b,也可以從子應(yīng)用a跳轉(zhuǎn)到子應(yīng)用b的指定路由中,這點在其他方案中暫時還沒看到有。

          并且如果子應(yīng)用a要跳轉(zhuǎn)到子應(yīng)用b是保活應(yīng)用,并且已經(jīng)進行過初始化了,那也有對應(yīng)的方案使子應(yīng)用b的狀態(tài)不會因為跳轉(zhuǎn)而丟失


          降級處理

          wujie 和 micro-app 的發(fā)布時間其實相距不遠(yuǎn),并且兩者都用到了webComponent的能力,但是wujie 對于webComponent 特性不支持的情況做了無感知的降級方案,但是micro-app沒有

          結(jié)束

          以上內(nèi)容純屬個人觀點,僅供討論,不保證內(nèi)容正確性

          點擊關(guān)注公眾號,“技術(shù)干貨” 及時達(dá)!

          瀏覽 173
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本久久网| 亚洲 欧美 人妻 | 亲子乱一区二区三区电影 | 青娱乐最新官网 | av中文字幕第一页 |