<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>

          字節(jié)跳動(dòng)是如何落地微前端的

          共 15327字,需瀏覽 31分鐘

           ·

          2021-10-26 23:02

          ?? 加個(gè)關(guān)注,后續(xù)上新不錯(cuò)過~


          本文內(nèi)提及的 Garfish 微前端解決方案已開源:https://github.com/modern-js-dev/garfish(目前的 Garfish 作為字節(jié)跳動(dòng)各部門應(yīng)用最廣泛的微前端解決方案已經(jīng)服務(wù)超過 100+ 前端團(tuán)隊(duì),400+ 項(xiàng)目),另外字節(jié)跳動(dòng)的現(xiàn)代 Web 工程體系即將開源(Modern.js),深度集成 Garfish 提供了對微前端的原生支持,提供更開箱即用的能力,敬請期待!

          微前端的出現(xiàn)的背景和意義

          微前端是什么:微前端是一種類似于微服務(wù)的架構(gòu),是一種由獨(dú)立交付的多個(gè)前端應(yīng)用組成整體的架構(gòu)風(fēng)格,將前端應(yīng)用分解成一些更小、更簡單的能夠獨(dú)立開發(fā)、測試、部署的應(yīng)用,而在用戶看來仍然是內(nèi)聚的單個(gè)產(chǎn)品。

          微前端誕生在兩個(gè)大的背景下,在提倡擁抱變化的前端社區(qū)可以看到新的框架、技術(shù)、概念層出不窮,并且隨著 Web 標(biāo)準(zhǔn)的演進(jìn),前端應(yīng)用已經(jīng)具備更好的性能、更快的開發(fā)效率。但隨著而來的是應(yīng)用的復(fù)雜程度更高、涉及的團(tuán)隊(duì)規(guī)模更廣、更高的性能要求,應(yīng)用復(fù)雜度已經(jīng)成為阻塞業(yè)務(wù)發(fā)展的重要瓶頸。

          微前端就是誕生于 Web 應(yīng)用日益復(fù)雜化的場景中,因?yàn)殡S著網(wǎng)絡(luò)速度、計(jì)算機(jī)硬件水平的提升和 Web 標(biāo)準(zhǔn)的演進(jìn),過去 Web 應(yīng)用用戶體驗(yàn)遠(yuǎn)不如傳統(tǒng)的應(yīng)用軟件時(shí)代已逐漸遠(yuǎn)去,兩者之間在用戶體驗(yàn)上的差距不斷縮減,并且由于 Web 應(yīng)用開發(fā)速度快、用完即走等特性,導(dǎo)致的一個(gè)最終結(jié)果就是「能用 Web 技術(shù)實(shí)現(xiàn)的應(yīng)用,最終都會(huì)通過 Web 來實(shí)現(xiàn)」。在近幾年涌現(xiàn)了一大批之前只能在傳統(tǒng) PC 軟件中才能看到的優(yōu)秀產(chǎn)品,例如:Photoshop、Web Office、Web IDE。盡管隨著 Web 標(biāo)準(zhǔn)的演進(jìn),前端工程化也在不斷演變,從模塊化到組件化在到現(xiàn)在的工程化,但在面對跨團(tuán)隊(duì)大規(guī)模開發(fā)、跨團(tuán)隊(duì)企業(yè)級應(yīng)用協(xié)作,現(xiàn)有的分治設(shè)計(jì)模式仍然顯得有心無力。


          大規(guī)模 Web 應(yīng)用的困局

          盡管 Web 應(yīng)用的復(fù)雜度和參與人數(shù)以爆炸式的增長速度,但卻沒有一種新的架構(gòu)模式來解決現(xiàn)有的困境,并同時(shí)兼顧 DX(developer experience)和 UX(user experience)。

          以字節(jié)跳動(dòng)內(nèi)「研發(fā)中臺(tái)」舉例,在研發(fā)日常工作中需要使用非常多的研發(fā)系統(tǒng),例如:代碼管理、代碼構(gòu)建、域名管理、應(yīng)用發(fā)布、CDN 資源管理、對象存儲(chǔ)等。站在整個(gè)公司研發(fā)的角度考慮,最好的產(chǎn)品形態(tài)就是將所有的研發(fā)系統(tǒng)都放置同一個(gè)產(chǎn)品內(nèi),用戶是無法感知他在使用不同的產(chǎn)品,對于用戶而言就是單個(gè)產(chǎn)品不存割裂感,也不需要去學(xué)習(xí)多個(gè)平臺(tái),僅僅需要學(xué)習(xí)和了解字節(jié)跳動(dòng)內(nèi)的「研發(fā)中臺(tái)」即可。

          在字節(jié)跳動(dòng)內(nèi)這一類應(yīng)用隨處可見,由于字節(jié)跳動(dòng)內(nèi)存在大量業(yè)務(wù)線,每一條業(yè)務(wù)線都會(huì)誕生大量的中臺(tái)系統(tǒng),并且還在指數(shù)增長,以字節(jié)跳動(dòng)內(nèi)電商業(yè)務(wù)舉例,對于電商運(yùn)營的日常工作來說,其實(shí)與研發(fā)日常工作一樣,圍繞在:商品、商家、品牌、風(fēng)控、營銷等工作上,那么對于電商運(yùn)營來說怎么樣才最高效的電商運(yùn)營系統(tǒng)呢,由于整個(gè)系統(tǒng)涉及范圍較廣,在實(shí)際的研發(fā)過程中必然會(huì)以功能或業(yè)務(wù)需求垂直的切分成更小的子系統(tǒng),切分成各種小系統(tǒng)后盡管由于分治的設(shè)計(jì)理念提升了開發(fā)者體驗(yàn),但是一定程度上降低了用戶體驗(yàn)。那能否以一種新的架構(gòu)模式,既保開發(fā)者體驗(yàn),又能提升用戶體驗(yàn)?zāi)亍?/p>

          傳統(tǒng) Web 應(yīng)用的利與弊

          這里簡單分析一下傳統(tǒng) Web 應(yīng)用在開發(fā)大規(guī)模應(yīng)用和涉及多研發(fā)團(tuán)隊(duì)協(xié)作時(shí)遇到的一些困境,以上面案例中的「電商運(yùn)營平臺(tái)」舉例,對于電商運(yùn)營而言商品、商家、品牌等都是電商運(yùn)營平臺(tái)能力的一部分,而不是獨(dú)立之間的孤島。若以傳統(tǒng)的前端研發(fā)模式進(jìn)行開發(fā),那么此時(shí)有兩種項(xiàng)目設(shè)計(jì)策略:

          1. 將平臺(tái)內(nèi)多個(gè)系統(tǒng)放置同一個(gè)代碼倉庫維護(hù) ,采用 SPA(Single-page Application) 單頁應(yīng)用模式
          2. 將系統(tǒng)分為多個(gè)倉庫維護(hù),在首頁聚合所有平臺(tái)的入口,采用 MPA(Multi-page Application)多頁應(yīng)用模式

          若采用多個(gè)系統(tǒng)放置同一個(gè)項(xiàng)目內(nèi)維護(hù):

          • 優(yōu)勢:
            • 更好的性能
            • 具備局部更新,無縫的用戶體驗(yàn)
            • 提前預(yù)加載用戶下一頁的內(nèi)容
            • 統(tǒng)一的權(quán)限管控、統(tǒng)一的 Open API 開發(fā)能力
            • 更好的代碼復(fù)用,基礎(chǔ)庫復(fù)用
            • 統(tǒng)一的運(yùn)營管理能力
            • 不同系統(tǒng)可以很好的通信
            • SPA 應(yīng)用特有優(yōu)勢:
          • 劣勢:
            • 代碼權(quán)限管控問題
            • 項(xiàng)目構(gòu)建時(shí)間長
            • 需求發(fā)布相互阻塞
            • 代碼 commit 混亂、分支混亂
            • 技術(shù)體系要求統(tǒng)一
            • 無法同時(shí)灰度多條產(chǎn)品功能
            • 代碼回滾相互影響
            • 錯(cuò)誤監(jiān)控?zé)o法細(xì)粒度拆分

          采用方案一的劣勢非常明顯,在日常開發(fā)中研發(fā):代碼構(gòu)建半小時(shí)以上、發(fā)布需求時(shí)被需求阻塞、無法局部灰度局部升級、項(xiàng)目遇到問題時(shí)回滾影響其他業(yè)務(wù)、無法快速引進(jìn)新的技術(shù)體系提高生產(chǎn)力,項(xiàng)目的迭代和維護(hù)對于研發(fā)同學(xué)而言無疑是噩夢。

          盡管降低了開發(fā)體驗(yàn),如果對項(xiàng)目整體的代碼拆分,懶加載控制得當(dāng),其實(shí)對于使用平臺(tái)的用戶而言體驗(yàn)卻是提升的,這一切都?xì)w因于 SPA 應(yīng)用帶來的優(yōu)勢,SPA 應(yīng)用跳轉(zhuǎn)頁面時(shí)無需刷新整個(gè)頁面,路由變化時(shí)僅更新局部,不用讓用戶產(chǎn)生在 MPA 應(yīng)用切換時(shí)整個(gè)頁面刷新帶來的抖動(dòng)感而降低體驗(yàn),并且由于頁面不刷新的特性可以極大程度的復(fù)用頁面間的資源,降低切換頁面時(shí)帶來的性能損耗,用戶也不會(huì)感知他在使用不同平臺(tái)。

          若采用拆分成多個(gè)倉庫維護(hù)

          • 優(yōu)勢
            • 可以以項(xiàng)目維度拆分代碼,解決權(quán)限管控問題
            • 僅構(gòu)建本項(xiàng)目代碼,構(gòu)建速度快
            • 可以使用不同的技術(shù)體系
            • 不存在同一個(gè)倉庫維護(hù)時(shí)的 commit 混亂和分支混亂等問題
            • 功能灰度互不影響
          • 劣勢
            • 用戶在使用時(shí)體驗(yàn)割裂,會(huì)在不同平臺(tái)間跳轉(zhuǎn),無法達(dá)到 SPA 應(yīng)用帶來的用戶體驗(yàn)
            • 只能以頁面維度拆分,無法拆分至區(qū)塊部分,只能以業(yè)務(wù)為維度劃分
            • 多系統(tǒng)同灰度策略困難
            • 公共包基礎(chǔ)庫重復(fù)加載
            • 不同系統(tǒng)間不可以直接通信
            • 公共部分只能每個(gè)系統(tǒng)獨(dú)立實(shí)現(xiàn),同一運(yùn)維通知困難
            • 產(chǎn)品權(quán)限無法進(jìn)行統(tǒng)一收斂

          采用方案二在一定程度上提升了開發(fā)體驗(yàn),但卻降低了用戶體驗(yàn),研發(fā)在日常開發(fā)工作中需要使用大量的平臺(tái),但是卻需要跳轉(zhuǎn)到不同的平臺(tái)上進(jìn)行日常的研發(fā)工作,整體使用體驗(yàn)較差。體驗(yàn)較差的原因在于將由于通過項(xiàng)目維度拆分了整體「研發(fā)中臺(tái)」這樣的一個(gè)產(chǎn)品,使各個(gè)產(chǎn)品之間是獨(dú)立的孤島,系統(tǒng)間相互跳轉(zhuǎn)都是傳統(tǒng)意義上的 MPA,跳轉(zhuǎn)需要重新加載整個(gè)頁面的資源,除了性能是遠(yuǎn)不如 SPA 應(yīng)用的并且應(yīng)用間是沒法直接通信,這就進(jìn)一步增強(qiáng)了用戶在使用產(chǎn)品時(shí)的割裂感。

          背景和意義總結(jié)

          通過以上兩個(gè)場景案例,其實(shí)可以發(fā)現(xiàn)由于 Web 應(yīng)用在逐步取代傳統(tǒng)的 PC 軟件時(shí),大規(guī)模 Web 應(yīng)用在面對高復(fù)雜度和涉及團(tuán)隊(duì)成員廣下無法同時(shí)保證 DX 和 UX 的困境。傳統(tǒng)的分而治之的策略已經(jīng)無法應(yīng)對現(xiàn)代 Web 應(yīng)用的復(fù)雜性,因此衍生出了微前端這樣一種新的架構(gòu)模式,與后端微服務(wù)相同,它同樣是延續(xù)了分而治之的設(shè)計(jì)模式,不過卻以全新的方法來實(shí)現(xiàn)。

          微前端解決方案

          上一節(jié)總結(jié)了微前端出現(xiàn)的背景和意義,并且了解了兩種傳統(tǒng) Web 應(yīng)用的研發(fā)模式:SPA(Single-page Application)、MPA(Multi-page Application)在涉及人員廣和項(xiàng)目復(fù)雜度高的場景下帶來的劣勢,那么期望能有一種新的架構(gòu)能同時(shí)具備 SPA 和 MPA 兩種架構(gòu)優(yōu)勢,并同時(shí)提升 ?DX(developer experience)和 UX(user experience)呢?

          那在理想的情況下,期望能達(dá)到,將一個(gè)復(fù)雜的單體應(yīng)用以功能或業(yè)務(wù)需求垂直的切分成更小的子系統(tǒng),并且能夠達(dá)到以下能力:

          • 子系統(tǒng)間的開發(fā)、發(fā)布從空間上完成隔離
          • 子系統(tǒng)可以使用不同的技術(shù)體系
          • 子系統(tǒng)間可以完成基礎(chǔ)庫的代碼復(fù)用
          • 子系統(tǒng)間可以快速完成通信
          • 子系統(tǒng)間需求迭代互不阻塞
          • 子應(yīng)用可以增量升級
          • 子系統(tǒng)可以走向同一個(gè)灰度版本控制
          • 提供集中子系統(tǒng)權(quán)限管控
          • 用戶使用體驗(yàn)整個(gè)系統(tǒng)是一個(gè)單一的產(chǎn)品,而不是彼此的孤島
          • 項(xiàng)目的監(jiān)控可以細(xì)化到到子系統(tǒng)

          那么基于上面理想情況,如何從零設(shè)計(jì)一套全新的架構(gòu)用于解決現(xiàn)代 Web 應(yīng)用在面對企業(yè)級系統(tǒng)遇到的困境呢。

          微前端的整體架構(gòu)

          那么如何提供一套既具備 SPA 的用戶體驗(yàn),又具備 MPA 應(yīng)用帶來的靈活性,并且可以實(shí)現(xiàn)應(yīng)用間同灰度,監(jiān)控也可以細(xì)化到子系統(tǒng)的解決方案呢?目前在字節(jié)跳動(dòng)內(nèi)應(yīng)用的微前端解決方案「Garfish」就是這樣的一套方案 ,該解決方案主要分為三層:部署側(cè)、框架運(yùn)行時(shí)、調(diào)試工具,采用的是 SPA 的架構(gòu)。

          解決方案整體架構(gòu)


          微前端部署平臺(tái)

          部署平臺(tái)作為微前端研發(fā)流程中重要的一環(huán),主要提供了:微前端的服務(wù)發(fā)現(xiàn)、服務(wù)注冊、子應(yīng)用版本控制、多個(gè)子應(yīng)用間同灰度、增量升級子應(yīng)用、下發(fā)子應(yīng)用信息列表,分析子應(yīng)用依賴信息提取公共基礎(chǔ)庫降低不同應(yīng)用的依賴重復(fù)加載。

          用于解決微前端中子應(yīng)用的獨(dú)立部署、版本控制和子應(yīng)用信息管理,通過 Serverless 平臺(tái)提供的接口或在渲染服務(wù)中下發(fā)主應(yīng)用的 HTML 內(nèi)容中包含子應(yīng)用列表信息,列表中包括了子應(yīng)用的詳細(xì)信息例如:應(yīng)用 id、激活路徑、依賴信息、入口資源等信息,并通過對于子應(yīng)用的公共依賴進(jìn)行分析,下發(fā)子應(yīng)用的公共依賴,在運(yùn)行時(shí)獲取到子應(yīng)用的信息后注冊給框架,然后在主應(yīng)用上控制子應(yīng)用進(jìn)行渲染和銷毀。


          微前端運(yùn)行時(shí)

          Why not iframe

          談到微前端繞不開的話題就是為什么不適用 iframe 作為承載微前端子應(yīng)用的容器,其實(shí)從瀏覽器原生的方案來說,iframe 不從體驗(yàn)角度上來看幾乎是最可靠的微前端方案了,主應(yīng)用通過iframe 來加載子應(yīng)用,iframe 自帶的樣式、環(huán)境隔離機(jī)制使得它具備天然的沙盒機(jī)制,但也是由于它的隔離性導(dǎo)致其并不適合作為加載子應(yīng)用的加載器,iframe 的特性不僅會(huì)導(dǎo)致用戶體驗(yàn)的下降,也會(huì)在研發(fā)在日常工作中造成較多困擾,以下總結(jié)了 iframe 作為子應(yīng)用的一些劣勢:

          • 使用iframe 會(huì)大幅增加內(nèi)存和計(jì)算資源,因?yàn)?iframe 內(nèi)所承載的頁面需要一個(gè)全新并且完整的文檔環(huán)境
          • iframe 與上層應(yīng)用并非同一個(gè)文檔上下文導(dǎo)致
            • 主應(yīng)用劫持快捷鍵操作
            • 事件無法冒泡頂層,針對整個(gè)應(yīng)用統(tǒng)一處理時(shí)效
            • 事件冒泡不穿透到主文檔樹上,焦點(diǎn)在子應(yīng)用時(shí),事件無法傳遞上一個(gè)文檔流
            • 跳轉(zhuǎn)路徑無法與上層文檔同步,刷新丟失路由狀態(tài)
            • iframe 內(nèi)元素會(huì)被限制在文檔樹中,視窗寬高限制問題
            • iframe 登錄態(tài)無法共享,子應(yīng)用需要重新登錄
            • iframe 在禁用三方 cookie 時(shí),iframe 平臺(tái)服務(wù)不可用
            • iframe 應(yīng)用加載失敗,內(nèi)容發(fā)生錯(cuò)誤主應(yīng)用無法感知
            • 難以計(jì)算出 iframe 作為頁面一部分時(shí)的性能情況
          • 無法預(yù)加載緩存 iframe 內(nèi)容
          • 無法共享基礎(chǔ)庫進(jìn)一步減少包體積
          • 事件通信繁瑣且限制多

          基于 SPA 的微前端架構(gòu)

          盡管難以將 iframe 作為微前端應(yīng)用的加載器,但是卻可以參考其設(shè)計(jì)思想,一個(gè)傳統(tǒng)的 iframe 加載文檔的能力可以分為四層:文檔的加載能力、HTML 的渲染、執(zhí)行 JavaScript、隔離樣式和 JavaScript 運(yùn)行環(huán)境。那么微前端庫的基礎(chǔ)能力也可以參考其設(shè)計(jì)思想。

          從設(shè)計(jì)層面采取的是基座+子應(yīng)用分治的概念,部署平臺(tái)負(fù)責(zé)進(jìn)行服務(wù)發(fā)現(xiàn)和服務(wù)注冊,將注冊的應(yīng)用列表信息下發(fā)至基座,通過基座來動(dòng)態(tài)控制子系統(tǒng)的渲染和銷毀,并提供集中式的模式來完成應(yīng)用間的通信和應(yīng)用的公共依賴管理,因此 Garfish 在 Runtime 層面主要提供了以下四個(gè)核心能力:

          • 加載器(Loader)
            • HTML 入口類型,拆解 HTML Dom、Script、Style
            • JS 入口類型,提供基礎(chǔ) Dom 容器
            • 負(fù)責(zé)注冊平臺(tái)側(cè)提供的應(yīng)用列表
            • 負(fù)責(zé)加載和解析子應(yīng)用入口資源
            • 預(yù)加載能力
            • 解析子應(yīng)用導(dǎo)出內(nèi)容
          • 沙箱隔離(Sandbox)
            • 提供代碼執(zhí)行能力,收集執(zhí)行代碼時(shí)存在的副作用
            • 提供銷毀收集副作用的能力
            • 支持沙箱多實(shí)例,收集不同實(shí)例的副作用
          • 路由托管(Router)
            • 解決不同應(yīng)用間的路由不同步問題
            • 提供路由劫持能力,在主應(yīng)用上管控子應(yīng)用路由
            • 提供路由驅(qū)動(dòng)能力來拼裝完整的平臺(tái)的能力
          • 子應(yīng)用通信(Store)
            • 建立通信橋梁
            • 提供共享機(jī)制

          應(yīng)用生命周期

          整個(gè)微前端子應(yīng)用的生命周期基本可以總結(jié)為:

          • 渲染階段
            • 若入口類型為 HTML 類型,將開始解析和拆解子應(yīng)用資源
            • 若入口類型為 JS,創(chuàng)建子應(yīng)用的掛點(diǎn) DOM
            • 主應(yīng)用通過路由驅(qū)動(dòng)或手動(dòng)掛載的方式觸發(fā)子應(yīng)用渲染
            • 開始加載應(yīng)用的資源內(nèi)容,并初始化子應(yīng)用的沙箱運(yùn)行時(shí)環(huán)境
            • 判斷入口類型
            • 將子應(yīng)用存在”副作用“(對當(dāng)前頁面可能產(chǎn)生影響的內(nèi)容)交由沙箱處理
            • 開始渲染子應(yīng)用的 DOM 樹
            • 觸發(fā)子應(yīng)用的渲染 Hook
          • 銷毀階段
            • 若路由變化離開子應(yīng)用的激活范圍或主動(dòng)觸發(fā)銷毀函數(shù),觸發(fā)應(yīng)用的銷毀
            • 清除應(yīng)用在渲染時(shí)和運(yùn)行時(shí)產(chǎn)生的副作用
            • 移除子應(yīng)用的 DOM 元素

          加載器的設(shè)計(jì)

          加載器的整體設(shè)計(jì)理念其實(shí)與 React-loadable 非常類似,具備以下能力:

          • 異步加載組件資源
          • 可以預(yù)加載資源
          • 可以緩存組件資源
          • 緩存組件實(shí)例

          與組件不同的是微前端作為一種能夠?qū)误w應(yīng)用拆解成多個(gè)子應(yīng)用的架構(gòu)模式,不同于組件,這些被拆分出去的子應(yīng)用最好的研發(fā)模式是在開發(fā)、測試、部署都與宿主環(huán)境分離,子應(yīng)用本身應(yīng)具備自治能力,那么此時(shí)就與 iframe 提供的能力非常類似,iframe 通過加載 HTML 文檔的形式加載整個(gè)子應(yīng)用的資源,那么子應(yīng)用本身就可作為一個(gè)獨(dú)立站點(diǎn),天然具備獨(dú)立開發(fā)、測試的能力。因此 Garfish 的加載器提供了兩種應(yīng)用入口類型:HTML 類型和 JS 入口類型,但需要注意的是 Garfish 并非像 iframe 一樣將其分為了另一個(gè)文檔流,而是將其與主應(yīng)用作為同一個(gè)文檔流處理,用以規(guī)避其不再同一個(gè)文檔流帶來的體驗(yàn)感割裂問題。

          由于 HTML 入口類型天然具備獨(dú)立、開發(fā)、測試的特性,在微前端整體架構(gòu)設(shè)計(jì)中,對于跨團(tuán)隊(duì)協(xié)作而言,最好的研發(fā)模式是能降低其溝通成本,而降低溝通成本的最好方式是不溝通,所以一般項(xiàng)目類型都盡可能的推薦用戶使用 HTML 的入口類型。

          那么針對 HTML 入口類型的加載器需要做一些什么呢,下面是一張瀏覽器的渲染過程圖:


          針對瀏覽器的渲染過程也可將其分為:HTML 文本下載、 HTML 拆解為語法樹、拆解語法樹中具備”副作用的內(nèi)容“(對當(dāng)前頁面可能產(chǎn)生影響的內(nèi)容)如 Script、Style、Link 并交由沙箱處理進(jìn)行后渲染,與一般的子應(yīng)用不同的是需要子應(yīng)用提供 provider,provider 中包含了子應(yīng)用渲染和銷毀的生命周期,這兩個(gè) Hook 可以應(yīng)用緩存模式中進(jìn)一步增強(qiáng)應(yīng)用的渲染速度和性能。


          沙箱的設(shè)計(jì)

          為什么需要沙箱

          其實(shí)在過去的 Web 應(yīng)用中是很少提及到沙箱這一概念的,因?yàn)榻M件的開發(fā)一般都會(huì)由研發(fā)通過研發(fā)規(guī)范來盡可能的去避免組件對當(dāng)前應(yīng)用環(huán)境造成副作用,諸如:組件渲染后添加了定時(shí)器、全局變量、滾動(dòng)事件、全局樣式并且在組件銷毀后會(huì)及時(shí)的清除子應(yīng)用對當(dāng)前環(huán)境產(chǎn)生的副作用。

          與組件完全不同的是微前端是由多個(gè)獨(dú)立運(yùn)行的應(yīng)用組成的架構(gòu)風(fēng)格,這些系統(tǒng)可能分別來自不同的技術(shù)體系。項(xiàng)目的開發(fā)、測試從空間和時(shí)間上都是分離的,由于沒有 iframe 一樣原生能力的隔離很難應(yīng)用間不發(fā)生沖突,這些沖突可能會(huì)導(dǎo)致應(yīng)用發(fā)生異常、報(bào)錯(cuò)、甚至不可用等狀態(tài)。

          以 Webpack4 JsonpFunction 為例

          在 Webpack5 中提供了一個(gè)重要的功能就是 Module Federation,隨著 Webpack 5 推出 Module Federation ,與 Webpack 4 發(fā)生變化的一個(gè)重要配置就是 JsonpFunction 屬性變?yōu)榱?chunkLoadingGlobal,并且由原來的默認(rèn)值 webpackJsonp 變成了默認(rèn)使用 output.library 名稱或者上下文中的 package.json 的 包名稱(package name)作為唯一值(webpack.js.org/issues/3940)。

          為什么會(huì)發(fā)生這個(gè)轉(zhuǎn)變呢,如果了解過 Webpack 構(gòu)建產(chǎn)物的一定會(huì)知道 Webpack 通過全局變量存儲(chǔ)了分 chunk 后的產(chǎn)物,用于解決分包 chunk 的加載問題。由于 Webpack 5 引入 Module Federation 頁面中可能會(huì)同時(shí)存在兩個(gè)以上的 Webpack 構(gòu)建產(chǎn)物,如果還是通過是通過同一個(gè)變量存儲(chǔ)要加載的 chunk ,必然會(huì)造成產(chǎn)物之間的互相影響。

          通過 Webpack 4 到 Webpack 5 支持 Module Federation 之后可以發(fā)現(xiàn),在一個(gè)基礎(chǔ)庫尚未考慮默認(rèn)兼容多實(shí)例的場景下,貿(mào)然將其作為多實(shí)例使用很可能會(huì)造成應(yīng)用無法按照預(yù)期運(yùn)行,更為嚴(yán)重的是你以為其正常運(yùn)行了其實(shí)應(yīng)用已經(jīng)發(fā)生了嚴(yán)重的內(nèi)存泄漏或不可預(yù)知的情況,倘若將 Webpack 構(gòu)建產(chǎn)物的應(yīng)用多次動(dòng)態(tài)的在頁面中運(yùn)行,將會(huì)發(fā)現(xiàn)已經(jīng)造成嚴(yán)重的內(nèi)存泄漏,因?yàn)?Webpack 會(huì)增量的向全局存儲(chǔ) chunk 的變量上掛載模塊以及依賴信息,簡單來說就是每次執(zhí)行 Webpack 構(gòu)建的子應(yīng)用代碼都會(huì)向 webpackJsonp 數(shù)組 push 大量的數(shù)據(jù),最終造成內(nèi)存泄漏,直至頁面崩潰。

          沙箱的核心能力

          為了保證應(yīng)用能夠穩(wěn)定的運(yùn)行且互不影響,需要提供安全的運(yùn)行環(huán)境,能夠有效地隔離、收集、清除應(yīng)用在運(yùn)行期間所產(chǎn)生的副作用,那應(yīng)用運(yùn)行期間主要會(huì)產(chǎn)生哪些副作用呢,可以將其分為以下幾類:全局變量、全局事件、定時(shí)器、網(wǎng)絡(luò)請求、localStorage、Style 樣式、DOM 元素。

          在 Garfish Runtime 中的沙箱主要能力也是圍繞在這一塊的能力建設(shè)上,針對子應(yīng)用可能產(chǎn)生的副作用類型主要分為兩類,一類是:靜態(tài)副作用、另一類則是:動(dòng)態(tài)副作用。這里靜態(tài)副作用和動(dòng)態(tài)副作用分別指的是什么呢,靜態(tài)副作用指的是 HTML 中靜態(tài)標(biāo)簽內(nèi)容例如:Script 標(biāo)簽、Style 標(biāo)簽、Link 標(biāo)簽,這些內(nèi)容屬于在 HTML 文檔流中就包含的,另外一部分副作用屬于動(dòng)態(tài)副作用,動(dòng)態(tài)副作用指的是由 JavaScript 動(dòng)態(tài)創(chuàng)建出來的,例如 JavaScript 可以動(dòng)態(tài)創(chuàng)建 Style、動(dòng)態(tài)創(chuàng)建 Script、動(dòng)態(tài)創(chuàng)建 Link、動(dòng)態(tài)執(zhí)行代碼、動(dòng)態(tài)添加 DOM 元素、添加全局變量、添加定時(shí)器、網(wǎng)絡(luò)請求、localStorage 等對當(dāng)前頁面產(chǎn)生副作用的內(nèi)容。

          針對子應(yīng)用的靜態(tài)副作用的收集比較簡單,Loader 核心模塊上已經(jīng)提供了子應(yīng)用入口資源類型的分析和拆解,可以從子應(yīng)用 DOM 樹中輕松拆解獲取副作用內(nèi)容,那么對于靜態(tài)副作用已經(jīng)可以完成有效的收集、清除,但是尚未具備隔離的能力。動(dòng)態(tài)創(chuàng)建的副作用都是通過 JavaScript 來動(dòng)態(tài)創(chuàng)建的,需要收集到 JavaScript 運(yùn)行時(shí)產(chǎn)生的副作用,并提供副作用的隔離和銷毀能力。

          沙箱設(shè)計(jì)的兩種思路

          在 Garfish 微前端中,如何有效收集、隔離、清除應(yīng)用的副作用是保障應(yīng)用能夠平穩(wěn)運(yùn)行的核心能力之一。沙箱的主要能力也在于能夠捕獲動(dòng)態(tài)創(chuàng)建的副作用,對應(yīng)用的副作用進(jìn)行隔離和清除。

          那么如何能夠有效的捕獲到動(dòng)態(tài)創(chuàng)建的副作用、收集、并隔離呢?目前 Garfish 提供了兩種設(shè)計(jì)思路,一種是快照模式,另外一種是 VM 模式。

          快照沙箱

          顧名思義,在應(yīng)用運(yùn)行前通過快照的模式來保存當(dāng)前執(zhí)行環(huán)境,在應(yīng)用銷毀后恢復(fù)回應(yīng)用之前的執(zhí)行環(huán)境,用于實(shí)現(xiàn)應(yīng)用間副作用的隔離和清除。類似于 “SL 大法”,通過 save 存儲(chǔ)環(huán)境,通過 load 加載環(huán)境的模式。

          代碼實(shí)現(xiàn)思路


          核心設(shè)計(jì)思想簡述:

          1. 針對每一種副作用提供一個(gè) Patch 類,這個(gè)類需要提供 save 和 load 兩個(gè)方法
          2. Save 對應(yīng)著該副作用的環(huán)境快照存儲(chǔ),Load 對應(yīng)著銷毀該副作用的銷毀恢復(fù)環(huán)境
          3. 并且針對每一種 Patch 還可以存儲(chǔ)其在運(yùn)行期間發(fā)生的變化,在優(yōu)化場景時(shí)并不用所有代碼,僅恢復(fù)執(zhí)行環(huán)境即可

          VM 沙箱

          通過快照沙箱的最簡化的核心實(shí)現(xiàn)后可以發(fā)現(xiàn),它的設(shè)計(jì)理念依賴于整個(gè)代碼的執(zhí)行屬于線性的過程,即:存儲(chǔ)執(zhí)行環(huán)境=>執(zhí)行具備副作用的代碼=>恢復(fù)執(zhí)行環(huán)境,但在實(shí)際的場景中對于應(yīng)用的劃分并以頁面為維度劃分,同一個(gè)頁面可能存在多個(gè)應(yīng)用,所以它的執(zhí)行順序并非線性,可能同時(shí)存在多個(gè)快照沙箱的實(shí)例環(huán)境,也就是快照沙箱多實(shí)例,以下面代碼舉例:


          通過上面的代碼可以發(fā)現(xiàn),在同時(shí)運(yùn)行多個(gè)快照沙箱實(shí)例時(shí),在代碼執(zhí)行順序非線性的場景下,并不能有效的收集和處理應(yīng)用的副作用,也基于此快照沙箱無法使用在非線性呢多實(shí)例的場景中,因此也進(jìn)一步推出了 VM(virtual machine) 沙箱。

          維基百科關(guān)于 VM ?的解釋:在計(jì)算機(jī)科學(xué)中的體系結(jié)構(gòu)里,是指一種特殊的軟件,可以在計(jì)算機(jī)平臺(tái)和終端用戶之間創(chuàng)建一種環(huán)境,而終端用戶則是基于虛擬機(jī)這個(gè)軟件所創(chuàng)建的環(huán)境來操作其它軟件。虛擬機(jī)(VM)是計(jì)算機(jī)系統(tǒng)的仿真器,通過軟件模擬具有完整硬件系統(tǒng)功能的、運(yùn)行在一個(gè)完全隔離環(huán)境中的完整計(jì)算機(jī)系統(tǒng),能提供物理計(jì)算機(jī)的功能。

          在 Node 中也提供了 VM 模塊,不過不過不同于傳統(tǒng)的 VM,它并不具備虛擬機(jī)那么強(qiáng)的隔離性,并沒有從模擬完整的硬件系統(tǒng),僅僅將指定代碼放置了特定的上下文中編譯并執(zhí)行代碼,所以它無法用于不可信來源的代碼。

          參考 Node 中 VM 模塊的設(shè)計(jì),以及 JavaScript 詞法作用域 的特性,可以設(shè)計(jì)出 VM 沙箱,不過與傳統(tǒng)的 VM 差異也同樣存在,它并能執(zhí)行不可信的代碼,因?yàn)樗母綦x能力僅限于將其運(yùn)行在一個(gè)指定的上下文環(huán)境中。

          從而得出以下設(shè)計(jì)

          隔離環(huán)境需要哪些上下文

          針對副作用的類型:全局變量、全局事件、定時(shí)器、網(wǎng)絡(luò)請求、localStorage、Style 樣式、DOM 元素,分別提供了全新的執(zhí)行上下文:

          • Window
            • 用于隔離全局環(huán)境
          • document
            • 收集 DOM 副作用
            • 收集 Style 副作用,進(jìn)行處理
            • 收集 Script,繼續(xù)放置沙箱處理
            • 用于捕獲動(dòng)態(tài)創(chuàng)建的 DOM 節(jié)點(diǎn)、Style、Script
          • timeout、interval
            • 處理定時(shí)器
          • localstorage
            • 隔離 localStorage
          • listener
            • 收集全局事件

          新的執(zhí)行上下文哪里來

          新的執(zhí)行上下文有兩個(gè)來源,

          • 來源于當(dāng)前環(huán)境
          • 來源于 iframe 的執(zhí)行環(huán)境

          由于 iframe 創(chuàng)建后需要需要較多的內(nèi)存資源和計(jì)算資源,而微前端中的 VM 沙箱并不需要一個(gè)完全的執(zhí)行上下文,所以可以基于當(dāng)前環(huán)境。


          快照沙箱和 VM 沙箱能力對比


          路由系統(tǒng)的設(shè)計(jì)

          在于現(xiàn)代 MVC 的設(shè)計(jì)思想,前端框架的設(shè)計(jì)思想也一直在發(fā)生變更,現(xiàn)代 Web 前端框架提供的最經(jīng)典的能力莫過于將 MVC 中的 Constroller 變?yōu)榱?Router,目前幾乎主流的前端框架都支持路由驅(qū)動(dòng)視圖,僅提供一個(gè) Router Map 路由表,無需關(guān)注控制任何路由狀態(tài)即可完成跳轉(zhuǎn)后的路由更新。

          通過微前端出現(xiàn)的背景和意義,可以了解到微前端主要是用于解決:應(yīng)用增量升級、多技術(shù)體系并存、構(gòu)建大規(guī)模企業(yè)級 Web 應(yīng)用而誕生的。那么在基于 SPA 的微前端架構(gòu)中也可以了解到,目前微前端主要是采用應(yīng)用分而治之 + 動(dòng)態(tài)加載 + SPA 應(yīng)用的模式來解決大規(guī)模應(yīng)用帶來的一系列問題。在以組件為顆粒度的 SPA 應(yīng)用中組件內(nèi)部是不需要關(guān)心路由的,但是在微前端中主要通過應(yīng)用維度來拆分,那么拆分的應(yīng)用也可能是一個(gè)獨(dú)立的 SPA 應(yīng)用,那么此時(shí)主應(yīng)用與子應(yīng)用的關(guān)系如何編排呢?

          微前端應(yīng)用中理想的路由調(diào)度

          假設(shè)存在一個(gè) Garfish 站點(diǎn),這個(gè)站點(diǎn)它是由主應(yīng)用+三個(gè)子應(yīng)用構(gòu)成,主應(yīng)用的 basename 為 /demo,并存在三個(gè) Tab 分別指向跳轉(zhuǎn)至不同的應(yīng)用,理想的路由效果:

          1. 在點(diǎn)擊 vue-app Tab,跳轉(zhuǎn)至 /demo/vue-app 路由后,分別激活 vue-app 下,為 Vue 類型的 A 應(yīng)用和 B 應(yīng)用,并激活 A 應(yīng)用和 B 應(yīng)用中的 Home 組件
          2. 點(diǎn)擊 React-app Tab 進(jìn)入到 /demo/react-app 路由后,分別激活 react-app 下,為 React 類型的 C 應(yīng)用,并激活 C 應(yīng)用的 Home 組件
          3. 在激活 C 應(yīng)用的基礎(chǔ)上,點(diǎn)擊 Detail 按鈕,跳轉(zhuǎn)至 /demo/react-app/detail,并激活 C 應(yīng)用的 detail 組件。
          4. 點(diǎn)擊瀏覽器返回按鈕展示,跳轉(zhuǎn) /demo/react-app/detail,并激活 C 應(yīng)用的 Home 組件,至此完成瀏覽器的基本路由跳轉(zhuǎn)能力。

          不考慮任何路由處理的場景

          假設(shè)存在一個(gè) Garfish 站點(diǎn),這個(gè)站點(diǎn)它是由主應(yīng)用+一個(gè)子應(yīng)用構(gòu)成。由于 Garfish 采用的是 ?SPA 架構(gòu),子應(yīng)用與主應(yīng)用所處于同一個(gè)執(zhí)行上下文,子應(yīng)用的路由原樣反應(yīng)在主應(yīng)用上。


          那么此時(shí)分別跳轉(zhuǎn)到:/home/detail路由會(huì)發(fā)現(xiàn)哪些問題呢?

          • 假定跳轉(zhuǎn)的方法可以同時(shí)觸發(fā)主子應(yīng)用路由更新,主應(yīng)用路由和子應(yīng)用路由會(huì)同時(shí)發(fā)生搶占情況,后渲染的組件會(huì)覆蓋先渲染的路由組件
          • 在觸發(fā)路由跳轉(zhuǎn)方后,只有主應(yīng)用視圖觸發(fā)刷新、只有子應(yīng)用視圖刷新、或都不刷新
            • 「視圖的路由狀態(tài)維護(hù)在框架內(nèi)部」,通過原生跳轉(zhuǎn)無法觸發(fā)視圖更新

          此時(shí)當(dāng)分別跳轉(zhuǎn)到:/home/detail/test 路由時(shí)分別觸發(fā)對應(yīng)的組件視圖,但是倘若子應(yīng)用路由中也存在 /detail視圖呢,由于應(yīng)用的開發(fā)采用分治的模式,應(yīng)用的開發(fā)從空間和時(shí)間上都是分離的,無法保證應(yīng)用間的路由不發(fā)生路由搶占的情況。

          「通過 history 路由跳轉(zhuǎn)無法保證應(yīng)用能夠觸發(fā)視圖更新」,在通過 history api 進(jìn)行路由跳轉(zhuǎn)時(shí),是無法觸發(fā)應(yīng)用視圖更新,假設(shè)存在一個(gè) React 應(yīng)用 A,存在一個(gè)組件視圖 Test,分別通過 React 提供的路由方法跳轉(zhuǎn)和原生的路由跳轉(zhuǎn)進(jìn)行觀察:


          Hash 和 History 路由模式

          目前主流的 SPA 前端應(yīng)用基本上都支持兩種路由模式,一種是:hash 模式、另一種則是 History 路由模式,兩者的優(yōu)劣和使用并不在本文的討論范圍之內(nèi),這里僅做在微前端這種分離式開發(fā)模式下的介紹,在微前端這種分離式 SPA 應(yīng)用開發(fā)的模式下該選擇哪種路由模式,以及多 SPA 應(yīng)用下他們的路由應(yīng)該如何編排:

          假設(shè)站點(diǎn)地址為:http://garfish.bytedance.net

          正常路由情況

          • 主應(yīng)用 history 模式、子應(yīng)用 history 模式

            • 主應(yīng)用(basename: /example):

              • 主應(yīng)用所有路由基于:http://garfish.bytedance.net/example

              • 例如跳轉(zhuǎn)到:/appA,http://garfish.bytedance.net/example/appA/

            • 子應(yīng)用(basename: /example/appA):

              • 子應(yīng)用所有路由基于:http://garfish.bytedance.net/example/appA

              • 跳轉(zhuǎn)到子應(yīng)用的 /detail 頁,http://garfish.bytedance.net/example/appA/detail

            • 特點(diǎn):

              • 當(dāng)主子應(yīng)用分別為 history 模式時(shí),子應(yīng)用的路由基于主應(yīng)用基礎(chǔ)路由并帶上自己的業(yè)務(wù)路由

              • 路由同步到主應(yīng)用路由上,通過 子應(yīng)用 scope 命名空間隔離(子應(yīng)用 A,提供 appA 的 scope)主應(yīng)用和其他應(yīng)用的路由沖突,并將子應(yīng)用

              • 路徑符合用戶和開發(fā)者認(rèn)知和理解

              • 支持嵌套層級使用,并繼續(xù)通過 scope 的命名空間保證路由可讀


          • 主應(yīng)用 history 模式、子應(yīng)用 hash 模式

            • 主應(yīng)用(basename: /example):

              • 主應(yīng)用所有路由基于:http://garfish.bytedance.net/example

              • 例如跳轉(zhuǎn)到:/appA,http://garfish.bytedance.net/example/appA/

            • 子應(yīng)用(basename: /example/appA):

              • 子應(yīng)用所有路由基于:http://garfish.bytedance.net/example/appA

              • 從主應(yīng)用:http://garfish.bytedance.net/example/appA,跳轉(zhuǎn)到子應(yīng)用的?/detail 頁,http://garfish.bytedance.net/example/appA#/detail

            • 特點(diǎn):

              • 在一定程度上具備主子應(yīng)用都為 history 模式的優(yōu)勢,不支持嵌套層級使用

              • 目前多數(shù)框架都不支持以 hash 值作為 basename

              • 可讀性尚可

          異常路由情況

          • 主應(yīng)用 hash 模式、子應(yīng)用 history 模式

            • 主應(yīng)用(basename: /example):

              • 主應(yīng)用所有路由基于:http://garfish.bytedance.net/example

              • 例如跳轉(zhuǎn)到:/detail,http://garfish.bytedance.net/example#/appA

            • 子應(yīng)用(basename: /example#/appA):

              • 子應(yīng)用所有路由基于:http://garfish.bytedance.net/example#/appA

              • 跳轉(zhuǎn)到子應(yīng)用的 /detail 頁,http://garfish.bytedance.net/example/detail#/appA

            • 特點(diǎn):

              • 「路由混亂」,不符合用戶和開發(fā)者直覺

              • 目前多數(shù)框架都不支持以 hash 值作為 basename


          • 主應(yīng)用 hash 模式、子應(yīng)用 hash 模式

            • 主應(yīng)用(basename: /example):

              • 主應(yīng)用所有路由基于:http://garfish.bytedance.net/example

              • 例如跳轉(zhuǎn)到:/detail,http://garfish.bytedance.net/example#/appA

            • 子應(yīng)用(basename: /example#/appA):

              • 子應(yīng)用所有路由基于:http://garfish.bytedance.net/example#/appA

              • 跳轉(zhuǎn)到子應(yīng)用的 /detail 頁,http://garfish.bytedance.net/example#/detail

            • 特點(diǎn):

              • 「路由混亂」,不符合用戶和開發(fā)者直覺

              • 目前多數(shù)框架都不支持以 hash 值作為 basename

              • 可能與主應(yīng)用或其他子應(yīng)用發(fā)生路由沖突

          Garfish ?Router 如何處理路由

          通過上面理想的路由模式案例發(fā)現(xiàn),微前端應(yīng)用拆分成子應(yīng)用后,子應(yīng)用路由應(yīng)具備自治能力,可以充分的利用應(yīng)用解耦后的開發(fā)優(yōu)勢,但與之對應(yīng)的是應(yīng)用間的路由可能會(huì)發(fā)生沖突、兩種路由模式下可能產(chǎn)生用戶難以理解的路由狀態(tài)、無法激活不同前端框架的下帶來的視圖無法更新等問題。

          目前 Garfish 主要提供了以下四條策略

          • 提供 Router Map,減少典型中臺(tái)應(yīng)用下的開發(fā)者理解成本
          • 為不同子應(yīng)用提供不同的 basename 用于隔離應(yīng)用間的路由搶占問題
          • 路由發(fā)生變化時(shí)能準(zhǔn)確激活并觸發(fā)應(yīng)用視圖更新
          Router Map 降低開發(fā)者理解成本

          在典型的中臺(tái)應(yīng)用中,通常可以將應(yīng)用的結(jié)構(gòu)分為兩塊,一塊是菜單另一塊則是內(nèi)容區(qū)域,依托于現(xiàn)代前端 Web 應(yīng)用的設(shè)計(jì)理念的啟發(fā),通過提供路由表來自動(dòng)化完成子應(yīng)用的調(diào)度,將公共部分作為拆離后的子應(yīng)用渲染區(qū)域。



          自動(dòng)計(jì)算出子應(yīng)用所需的 basename

          當(dāng)應(yīng)用處于激活狀態(tài)時(shí),根據(jù)應(yīng)用的激活條件自動(dòng)計(jì)算出應(yīng)用所需的基礎(chǔ)路徑,并在渲染時(shí)告訴框架,以便于應(yīng)用間路由不發(fā)生沖突。


          如何有效的觸發(fā)不同應(yīng)用間的視圖更新

          目前主流框架實(shí)現(xiàn)路由的方式并不是監(jiān)聽路由變化觸發(fā)組件更新,讓開發(fā)者通過框架包裝后的 API 進(jìn)行跳轉(zhuǎn),并內(nèi)部維護(hù)路由狀態(tài),在使用框架提供 API 方法發(fā)生路由更新時(shí),內(nèi)部狀態(tài)發(fā)生變更觸發(fā)組件更新。

          由于框架的路由狀態(tài)分別維護(hù)在各自的內(nèi)部,那么如何保證在路由發(fā)生變化時(shí)能及時(shí)有效的觸發(fā)應(yīng)用的視圖更新呢,答案是可以的,目前主要有兩種實(shí)現(xiàn)策略:

          1. 收集框架監(jiān)聽的 popstate 事件
          2. 主動(dòng)觸發(fā) popstate 事件

          因?yàn)槟壳爸С?SPA 應(yīng)用的前端框架都會(huì)監(jiān)聽瀏覽器后退事件,在瀏覽器后退時(shí)根據(jù)路由狀態(tài)觸發(fā)應(yīng)用視圖的更新,那么其實(shí)也可以利用這種能力主動(dòng)觸發(fā)應(yīng)用視圖的更新,可以通過收集框架的監(jiān)聽事件,也可以觸發(fā) popstate 來響應(yīng)應(yīng)用的 popstate 事件

          基于「現(xiàn)代 Web 框架」的微前端最佳實(shí)踐

          微前端作為一種全新的 Web 應(yīng)用類型,不同于以往傳統(tǒng)的 Web 應(yīng)用開發(fā),微前端需要采用主子應(yīng)用分治的開發(fā)模式后帶來了一系列新的挑戰(zhàn),這些挑戰(zhàn)包括但不限于:主子應(yīng)用開發(fā)調(diào)試、普通 Web 應(yīng)用如何快速變?yōu)槲⑶岸藨?yīng)用、如何支持微前端應(yīng)用 SSR、主子應(yīng)用數(shù)據(jù)通信觸發(fā)視圖更新。Modern.js 作為 Garfish 上層的現(xiàn)代 Web 框架,能夠很好的解決這些問題,并提供開箱即用的開發(fā)體驗(yàn)。

          微前端應(yīng)用的調(diào)試開發(fā)

          由于微前端應(yīng)用采用分治的開發(fā)策略,應(yīng)用間的維護(hù)和開發(fā)可能在時(shí)間和空間上都是分離的,那么在開發(fā)環(huán)境時(shí)啟動(dòng)整個(gè)微前端項(xiàng)目的所有主子應(yīng)用,是一個(gè)并不明智的策略,不僅需要 clone 其他倉庫并完成應(yīng)用的運(yùn)行,還要保證其代碼的時(shí)效性。Modern.js 提供了更優(yōu)的的策略:

          • 某些子應(yīng)用需要更新時(shí)
            • 主應(yīng)用線上環(huán)境
            • 需要開發(fā)的子應(yīng)用線下環(huán)境
            • 不需要開發(fā)的子應(yīng)用上線
          • 主應(yīng)用需要更新時(shí)
            • 主應(yīng)用線下環(huán)境
            • 所有子應(yīng)用線上環(huán)境

          通過以上更優(yōu)的調(diào)試策略,可以保證開發(fā)者僅運(yùn)行自己的關(guān)注的應(yīng)用即可。那么如何達(dá)到這種更優(yōu)的,可以采用應(yīng)用列表的下發(fā)模式,框架運(yùn)行時(shí)加載下發(fā)的應(yīng)用列表,在開發(fā)主應(yīng)用時(shí)拉取線上的應(yīng)用列表,在開發(fā)某個(gè)子應(yīng)用時(shí)代理代理列表中的資源為子應(yīng)用的列表。

          傳統(tǒng) Web 應(yīng)用支持微前端模式

          通過微前端運(yùn)行時(shí)章節(jié)可以發(fā)現(xiàn)傳統(tǒng) Web 應(yīng)用與微前端應(yīng)用間進(jìn)行切換成本并不高,但需要研發(fā)關(guān)注應(yīng)用的路由的調(diào)度、應(yīng)用的生命周期導(dǎo)出、額外的構(gòu)建配置、應(yīng)用通信數(shù)據(jù)觸發(fā)視圖更新,微前端模式應(yīng)用和傳統(tǒng) Web 應(yīng)用間如何進(jìn)行切換都存在一定的學(xué)習(xí)和理解成本。

          在 Modern.js 中作為上層框架集成了 Garfish,原生支持微前端應(yīng)用,可以通過簡單配置即可完成微前端應(yīng)用類型的轉(zhuǎn)換,幫助用戶快速搭建應(yīng)用基礎(chǔ)結(jié)構(gòu),以降低其學(xué)習(xí)成本,快速生成微前端應(yīng)用。

          微前端應(yīng)用如何支持 SSR

          微前端作為一種全新的架構(gòu)模式,其分治設(shè)計(jì)模式除了帶來的諸多優(yōu)點(diǎn)外,但與之對應(yīng)的是引入了新的問題,如何支持傳統(tǒng) Web 應(yīng)用提供的 SSR 能力,由于微前端采用了分治的開發(fā)模式,應(yīng)用拆分成了多個(gè)子應(yīng)用,那么需要實(shí)現(xiàn)整體應(yīng)用的 SSR 能力,則需要與具體的 Web 框架相結(jié)合,通過制定微前端應(yīng)用的加載規(guī)則,達(dá)到微前端應(yīng)用也能有效的實(shí)現(xiàn) SSR 能力。

          Modern.js 作為 Garfish 的上層框架,提供更開箱即用的上層能力 ,并解決了以上微前端不同于傳統(tǒng) Web 應(yīng)用開發(fā)后帶來的弊端,文末有關(guān)于 Modern.js 的發(fā)布預(yù)告,可以了解并關(guān)注。

          微前端的優(yōu)點(diǎn)

          • 適用于大規(guī)模 Web 應(yīng)用的開發(fā)
          • 更快的開發(fā)速度
          • 支持迭代可開發(fā)和增強(qiáng)升級
          • 拆解后的部分降低了開發(fā)者的理解成本
          • 同時(shí)具備 UX 和 DX 的開發(fā)模式

          微前端的缺點(diǎn)

          • 復(fù)雜度從代碼轉(zhuǎn)向基礎(chǔ)設(shè)施
          • 整個(gè)應(yīng)用的穩(wěn)定性和安全性變得更加不可控
          • 具備一定的學(xué)習(xí)和了解成本
          • 需要建立全面的微前端周邊設(shè)施,才能充分發(fā)揮其架構(gòu)的優(yōu)勢
            • 調(diào)試工具
            • 監(jiān)控系統(tǒng)
            • 上層 Web 框架
            • 部署平臺(tái)

          何時(shí)使用微前端

          • 大規(guī)模企業(yè)級 Web 應(yīng)用開發(fā)
          • 跨團(tuán)隊(duì)及企業(yè)級應(yīng)用協(xié)作開發(fā)
          • 長期收益高于短期收益
          • 不同技術(shù)選型的項(xiàng)目
          • 內(nèi)聚的單個(gè)產(chǎn)品中部分需要獨(dú)立發(fā)布、灰度等能力
          • 微前端的目標(biāo)并非用于取代 iframe
            • 應(yīng)用的來源必須可信
            • 用戶體驗(yàn)要求更高

          總結(jié)

          微前端概念的出現(xiàn)是前端發(fā)展的必然階段,PC 互聯(lián)網(wǎng)轉(zhuǎn)向移動(dòng)互聯(lián)網(wǎng)時(shí)代時(shí),PC 的場景并未完全被消滅,反而轉(zhuǎn)向了衍生出了更多沉浸感更高、體驗(yàn)感更強(qiáng)的應(yīng)用,與之對應(yīng)的應(yīng)該是出現(xiàn)新的架構(gòu)模式來應(yīng)對這些應(yīng)用規(guī)模的增長。

          微前端也并非銀彈,采用微前端后復(fù)雜度并未憑空消失,而是由代碼轉(zhuǎn)向了基礎(chǔ)設(shè)施,對架構(gòu)設(shè)計(jì)帶來了更大的挑戰(zhàn),并且在新的架構(gòu)下需要設(shè)計(jì)并提供更多的周邊工具和生態(tài)來助力這一新的研發(fā)模式。

          本文更多的是從背景和設(shè)計(jì)層面講清楚微前端解決方案應(yīng)具備哪些能力,以及核心模塊的設(shè)計(jì)。每一部分并未包含過于詳細(xì)的細(xì)節(jié),如果想要了解「微前端運(yùn)行時(shí)」詳細(xì)設(shè)計(jì),可以通過 https://github.com/modern-js-dev/garfish 倉庫了解細(xì)節(jié)。

          參考

          • 如何設(shè)計(jì)微前端中的主子路由調(diào)度:https://mp.weixin.qq.com/s/TAXP7ipDdtb2Jb-L3QHszA
          • 如何取巧實(shí)現(xiàn)一個(gè)沙箱:https://mp.weixin.qq.com/s/Mg3fU0WvZUQnlWHdxc-b5A
          • 微服務(wù)架構(gòu)及其最重要的 10 個(gè)設(shè)計(jì)模式:https://www.infoq.cn/article/kdw69bdimlx6fsgz1bg3
          • single-spa:https://github.com/single-spa/single-spa

          Modern.js 開源預(yù)告

          Modern.js 和 Garfish 都是字節(jié)跳動(dòng) Web Infra 發(fā)起的「現(xiàn)代 Web 工程體系」開源項(xiàng)目,Modern.js 原生支持微前端,在 Garfish 基礎(chǔ)上提供了完整的微前端最佳實(shí)踐。

          Modern.js 計(jì)劃在 10 月 14 號發(fā)布 1.0.0 版和上線文檔站,歡迎關(guān)注和參與。

          - END -

          ?? 點(diǎn)擊 「閱讀原文」,查看 Garfish 倉庫

          瀏覽 42
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  人人操人人射 | 啪啪啪免费网站 | 天堂资源福利在线 | 欧美三级视频在线 | 国产人人色|