<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的解決方案來簡(jiǎn)單聊聊微前端

          共 15320字,需瀏覽 31分鐘

           ·

          2024-07-15 09:15

          前言

          因?yàn)槟壳坝袝r(shí)間了,所以在整理一下自己這幾年寫過的一些東西的相關(guān)文檔,準(zhǔn)備把一些東西改一下發(fā)出來,有的內(nèi)容可能并不復(fù)雜,甚至有點(diǎn)淺顯,但是也是對(duì)自己這幾年的一些復(fù)盤和總結(jié)了

          如果有需要,轉(zhuǎn)載前請(qǐng)向我確認(rèn)

          另:剛?cè)肼毜墓景芽偙O(jiān)和經(jīng)理都干掉了,有可能因?yàn)榘l(fā)展路線原因不再需要前端,現(xiàn)在求一份廣州 、 深圳的前端開發(fā)工作

          本科5年經(jīng)驗(yàn),19年畢業(yè),18年開始從事前端工作,基礎(chǔ)良好、有千萬級(jí)日活產(chǎn)品開發(fā)維護(hù)經(jīng)驗(yàn),有大型產(chǎn)品開發(fā)經(jīng)驗(yàn),有良好的代碼風(fēng)格與文檔習(xí)慣

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

          什么是微前端

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

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

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

          等等,諸如此類的定義

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

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

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

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

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

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

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

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

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

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

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

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

          微前端適用于什么場(chǎng)景

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

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

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

          什么是松散耦合

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

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

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

          image.png

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

          「(1) 在進(jìn)入一個(gè)新團(tuán)隊(duì)的時(shí)候,經(jīng)常有可能接手到一個(gè) 5 年陳的項(xiàng)?,或者我們需要重啟一個(gè)多年未維護(hù)的項(xiàng)目」

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

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

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

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

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

          「例如」

          1. 因?yàn)閱误w應(yīng)用的不斷膨大導(dǎo)致的理解和修改成本的不斷上升

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

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

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

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

          等等。。。。。。。。

          2.快速驗(yàn)證

          其實(shí)我們碰到

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

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

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

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

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

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

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

          因?yàn)樗缮Ⅰ詈系南到y(tǒng)結(jié)構(gòu)可應(yīng)用的場(chǎng)景太多了,所以就不一一列舉了

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

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

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

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

          跟我們?cè)诘谝徊糠炙岬降囊粯樱⑶岸说膶?shí)現(xiàn)本質(zhì)上也是一種組合的思想,子應(yīng)用間的組合其實(shí)和功能中組件的組合有一定程度上的異曲同工之妙,那么我們組件之間的組合最重要的是什么?

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

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

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

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

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

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

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

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

          但是!

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

          因?yàn)槲⑶岸思軜?gòu),在應(yīng)用這一層級(jí)之上,目標(biāo)是「分開開發(fā),分開構(gòu)建,分開部署」

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          工作空間的獨(dú)立

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

          「js 沙箱」

          因?yàn)槲覀兠恳粋€(gè)子應(yīng)用都是一個(gè)單獨(dú)的項(xiàng)目和應(yīng)用,在同一個(gè)頁面中出現(xiàn)多個(gè)子應(yīng)用是很常見的場(chǎng)景

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

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

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

          「css 沙箱」

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

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

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


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

          預(yù)加載

          因?yàn)樵谖⑶岸说姆桨钢校總€(gè)子應(yīng)用都是獨(dú)立的,所以如果不做任何處理的話在一個(gè)頁面存在多個(gè)子應(yīng)用的情況下,每個(gè)子應(yīng)用的加載都是獨(dú)立的,在這種情況下我們就很容易讓用戶體驗(yàn)到不同功能之間陸續(xù)從白屏到加載完成的割裂過程。這個(gè)無疑是致命的缺陷。

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

          公共依賴的處理問題

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

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

          路由的管理

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

          路由劫持

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

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

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

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

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

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

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

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

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

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

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

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

          小結(jié)

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

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

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

          兩個(gè)問題:什么場(chǎng)景適用、怎么更好地拓展


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

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

          畢竟這只是一個(gè)四十分鐘不到的分享


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

          在我們需要一個(gè)微前端方案時(shí),我們需要考慮的問題有如下幾點(diǎn):

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

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

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

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

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

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

          (因?yàn)闀r(shí)間有限,并且不是此次分享重點(diǎn),所以不會(huì)過于詳細(xì))

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

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

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

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

          但是「缺點(diǎn)」也非常明顯:

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

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

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

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

          能力 現(xiàn)狀
          通訊與狀態(tài)管理 1、初始化狀態(tài)通過props傳入子組件
          2、qiankun通過initGlobalState, onGlobalStateChange, setGlobalState實(shí)現(xiàn)主應(yīng)?的全局狀態(tài)管理,然后默認(rèn)會(huì)通過props將通信?法傳遞給?應(yīng)?,但是本質(zhì)上還是通過發(fā)布 - 訂閱的方式來進(jìn)行通訊
          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、每個(gè)子應(yīng)用中注冊(cè),然后由主應(yīng)用進(jìn)行管理,主應(yīng)用的路由實(shí)例通過 props 傳給微應(yīng)用,微應(yīng)用這個(gè)路由實(shí)例跳轉(zhuǎn)
          2、注冊(cè)微應(yīng)用的基礎(chǔ)配置信息。當(dāng)瀏覽器 url 發(fā)生變化時(shí),會(huì)自動(dòng)檢查每一個(gè)微應(yīng)用注冊(cè)的 activeRule 規(guī)則,符合規(guī)則的應(yīng)用將會(huì)被自動(dòng)激活
          3、頁面上不能同時(shí)顯示多個(gè)依賴于路由的微應(yīng)用,因?yàn)闉g覽器只有一個(gè) url,如果有多個(gè)依賴路由的微應(yīng)用同時(shí)被激活,那么必定會(huì)導(dǎo)致其中一個(gè) 404。
          「(因?yàn)榛诼酚善ヅ洌詿o法同時(shí)激活多個(gè)子應(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屬性為每個(gè)樣式添加前綴,將子應(yīng)用的樣式影響禁錮在當(dāng)前標(biāo)簽區(qū)域,但是依舊沒辦法必定隔絕
          預(yù)加載 在瀏覽器空閑時(shí)間,依照開發(fā)者傳入的順序,依次加載每個(gè)應(yīng)用的靜態(tài)資源,以確保不會(huì)影響基座應(yīng)用的性能
          公共依賴的處理 沒有解決基座應(yīng)用和子應(yīng)用共用依賴的特性,issues中回答了在計(jì)劃中,但是還沒想好怎么做
          路由管理 每個(gè)應(yīng)用的路由實(shí)例都是不同的,應(yīng)用的路由實(shí)例只能控制自身,無法影響其它應(yīng)用,包括基座應(yīng)用無法通過控制自身路由影響到子應(yīng),路由跳轉(zhuǎn)只有三種方式:window.history,通過history.pushState或history.replaceState進(jìn)行跳轉(zhuǎn)、通過數(shù)據(jù)通信控制跳轉(zhuǎn)(基座控制子應(yīng)用跳轉(zhuǎn),子應(yīng)用監(jiān)聽基座數(shù)據(jù)變化而跳轉(zhuǎn))、傳遞路由實(shí)例方法,把實(shí)例傳到子應(yīng)用里去跳轉(zhuǎn)(子應(yīng)用控制基座跳轉(zhuǎn))
          url屬性和子應(yīng)用路由沒有關(guān)系,只是用來加載html資源
          html entry 支持, 實(shí)際上是以類WebComponent + HTML Entry實(shí)現(xiàn)微前端的組件化,但是webcomponents沒有做降級(jí)處理
          應(yīng)用保活 支持,< micro-app name='xx' url='xx' keep-alive>
          應(yīng)用嵌套 支持
          生命周期 支持


          Module Federation

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


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

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

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


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

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

          最重要的通訊問題

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


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

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

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

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

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

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

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

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

          「源碼」


          dom割裂的問題

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

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

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


          首次白屏的問題

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

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

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

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

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


          預(yù)加載的處理

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

          js和css隔離

          這個(gè)不用多言,懂的都懂

          公共依賴的處理

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

          路由管理

          跟其他方案不同的是,wujie的路由管理有兩個(gè)比較有意思的點(diǎn)

          「1、路由同步」

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

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

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

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

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


          降級(jí)處理

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

          結(jié)束

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

          往期推薦


          大廠面試官:我理想中的前端
          對(duì)話Svelte未來,Rust 編譯器?構(gòu)建大型應(yīng)用?
          收藏!史上最全 Vue 前端代碼風(fēng)格指南

          最后


          • 歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我
          瀏覽 172
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  免费观看一级二级网站 | 日韩黄色片免费看 | 中文字幕一区二区三区四虎在线 | 国产无遮挡又黄又爽又色学生软件 | 三级片视频在线播放 |