多是多端適配性最好的微前端框架

引子

對微前端方向有了解的同窗可能都看過 qiankun 的文檔,深刻的同窗估計也去翻過其代碼和 single-spa 的源碼。一年前我也是受微前端思想的影響,當時恰好面臨的業務是變化頻繁,須要支持線上功能的熱插拔和獨立部署。充分調研了微前端和 qiankun 以後,發現很適合當前業務,纔開始開始實踐微前端的。因爲 single-spa 太過於簡陋,就採用 qiankun@1.x 做爲微前端驅動,今天想把這段時間以來遇到的問題和解決之道跟你們分享一下。css

可能面臨的業務場景

假設你如今維護或新開發這樣一個項目,業務變化頻繁,頁面功能有重複,但風格是一致的,暫時只有 pc 端。你拿到設計稿後發現,能夠把業務分拆成多套 layout,由基座(主應用)提供。功能獨立的模塊集成在子應用,每一個子應用有特殊的 routerBase。html

咔咔咔,兩三下你作完了全部任務,子應用也能在線配置,支持熱插拔和獨立部署。爽歪歪~前端

幾天後忽然有了如下幾個新業務場景,你陷入了沉思。。。vue

場景一:隨着業務的變化,須要支持移動端。

你新建了個子應用,routerBase 設置爲 h5。當判斷是移動端時自動跳轉 /h5/xxx。若是 pc 端訪問 /h5/xxx 時,自動重定向到 /xxx,能勉強應付該場景,不過由於基座過重致使移動端渲染很慢,在移動端使用「前進/後退」按鈕也會由於重定向太多致使混亂,有時候頁面渲染成 pc 頁面或沒法渲染。react

場景二:頁面是響應式的,移動端點擊登陸後,也能共享登陸態。

響應式頁面是活動頁,有活動報名,就要求登陸態共享。當用戶在移動端點擊登陸時,理應跳轉到移動端的登陸頁面,登陸完重定向到活動頁面 須要是登陸成功的狀態。若是是每一個子應用都有 routerBase,來回跳轉好幾回,並且還要想辦法共享登陸。git

場景三:老闆要求 pc 端和移動端 URL 一致。。。

routerBase 自然斷絕了這種可能性,根本沒法作到!github

場景四:老闆要求一個四年前的項目聚合到門戶站點中。。。

該項目的不少依賴特別是腳手架都已經嚴重過期,鎖定一級依賴的版本但鎖不了二級以上的版本,致使構建發佈失敗。根本沒法往項目代碼中加入 bootstrap/mount/unmount...!chrome

qiankun 的「kun」(困)境

qiankun 的定位多是中後臺項目的聚合。這類項目彼此間不多依賴,登陸態的控制能夠在基座中完成,屬於一個基座下的多個串聯。但面臨上述場景時,就顯得力不從心。npm

另外上述場景也暴露了 qiankun 的諸多問題:json

  • 基座通常由真實的業務應用承載,提供登陸等功能,也提供多套 layout;
  • 基座很容易耦合不少 pc 端的 UI 庫和功能代碼;
  • 基座很容易淪爲通用數據和邏輯的彙集地,雖然這些不是基座應用的職責;
  • 嚴重依賴 routerBase,框架判斷便利但犧牲了靈活性;
  • 多實例共存支持不足,目前僅支持 microApp 形式,且這種應用不建議有路由系統;
  • 嚴重依賴生命週期 bootstrap/mount/unmount;

破局之道

@icatjs/micro 是我這四個月來實現並在實際項目中實踐的新一代微前端框架,不但能輕鬆應付上述「困境」,還具有其餘良好的特性。目前僅有兩個方法 registerSubapps 和 start ,其餘須要注意的就是子應用的配置了。TA 是如何應對上述困境的呢?聽我一一分享下:

端應用(或稱 layout 應用)

你們看到這個新名詞,不要覺得對子應用作了強制的分類。只要某個子應用 a 提供了 layout 或一些路徑被用做了 layout,又被其餘子應用 b 依賴了,那麼該應用 a 就能夠看作是「端應用」。a 是否是端應用,本身根本不知道,它會按子應用正常的加載。而 b 會在 a 提供了掛載點後再渲染到頁面上。看個例子吧~

這是端應用的配置

{
    "name": "a",
    "entry": "aaa",
    "history": "browser",
    "props": {},
    "rules": [
      {
        "rule": "/",
        "container": "#mountNode",
        "endType": "pc"
      }
    ]
  }

解析下:a 做爲 pc 端的應用,路徑規則配置的是通配符 / ,這樣全部的路由都會被這個應用匹配到。固然會經過 endType 優先判斷是哪一個端,而後再去處理應用的「依賴鏈」。目前因爲場景比較簡單,每一個路由下只支持一條依賴鏈。理論上講可支持多條依賴鏈,也就是 多鏈多實例同時並存。

這是子應用的配置

{
    "name": "b",
    "entry": "bbb",
    "history": "browser",
    "props": {},
    "rootVars": {
      "externals": {
        "@react": "React",
        "@react-dom": "ReactDOM",
        "userInfo": "userInfo"
      }
    },
    "rules": [
      {
        "rule": "/b/activities/1520",
        "layout": "a > /layout/headless",
        "endType": "none"
      },
      {
        "rule": "/b/tec-support",
        "container": "#mountNode",
        "endType": "none"
      },
      {
        "rule": "/development",
        "layout": "a > /layout/basic",
        "endType": "pc"
      }
    ]
  }

也解析下:能夠看 rules 中配置了三個典型的規則,第一條依賴了端應用 a 的 /layout/headless,第二條不依賴任何端應用,第三條依賴了 a 的 /layout/basic。

不配置 container 的路徑,若是是子應用默認是 #subappMountNodeWrapper ,若是是端應用不依賴任何 layout 則默認是 #appMountNodeAndDoNotCover 。這樣若是頁面中有這些節點出現時,子應用就會渲染到該節點上。

你們也能夠看到,一個子應用的激活與否只和路徑規則有關,這些規則遵照 single-spa 的 activeWhen 規則,能夠是字符串、函數或數組。而每條路徑規則都有本身的 container,也有屬於本身的端 endType 和 layout。這樣一個子應用既能夠做爲依賴鏈上的一個端應用,又能夠做爲獨立的子應用。按路徑規則靈活組合,適應多種場景須要。

注意!端應用也能夠依賴其餘端應用,只須要在其路徑規則上配置須要依賴的路徑便可。

entry

能夠是一個 html 文件路徑,也能夠是一個數組,包含多個 js、css 和 html。初始化掛載點時,以最後一個 html 的內容填充進去。

全局路由系統

內置處理了 react/vue 多個子應用同時被激活,多個路由系統衝突的問題。無需再像使用 qiankun 那樣,還要把子應用的路由配置到主應用中,以防衝突。全局路由會攔截路由變化,把真實路徑和 layout 路徑分發到各個子應用,使其能正常渲染。
而且,全部子應用均可以有本身獨立的 404 頁面。能夠把子應用徹底當作是獨立的應用,無需顧慮路由的默認匹配,無需擔憂路由不匹配時直接進入應用的 404 頁面。

沙箱

內置了 iframe 沙箱,子應用的代碼運行是在 iframe 中。這樣代碼的狀態都會保存在 iframe 中,不用麻煩提供快照,自然具有快照能力。也內置了垃圾回收隊列,當一個子應用 5 分鐘內不被激活,會被釋放掉 iframe。
注意!這些 iframe 都是空白 iframe,耗費的渲染資源不多。但框架在 iframe 中提供了虛擬 BOM,使這些字應用運行完的結果可以正確渲染到 iframe 外的真實渲染層中。
注意2!子應用的全部對 dom 的操做都被限制在掛載點中,換言之,document.body 指向的是掛載點而不是渲染層的真實 body。這種嚴密的控制也體如今 getElementById 等方法中。
image.png

固然還有其餘一些特性如第三方庫共享全局數據共享組件&方法&狀態流共享緩存機制等,初次介紹就先不深刻了。後續會籌備官網,全面介紹這些特性的。

到此,咱們梳理一下困境的解決之道:

  • 場景一,須要支持移動端。因爲去掉了 routerBase 的依賴,又支持端應用,自然解決了基座重形成的渲染問題,反覆重定向形成的跳轉問題。
  • 場景二,頁面是響應式的須要共享登陸態。因爲支持 layout 依賴鏈,某個端應用能夠集中處理登陸邏輯,其餘端應用依賴其提供的功能便可。邏輯能夠經過流共享模式或者全局數據共享,分發到多個子應用。響應式的頁面屬於依賴鏈的某個節點,天然能及時得到登陸態。
  • 場景三,URL 一致性。因爲去掉了 routerBase 的依賴,框架會在初始化時判斷所處的終端,進而激活不一樣端應用。而端應用的路徑規則均可以配置成通配符 / ,這樣很容易保證 URL 一致。
  • 場景四,四年前的老項目集成。框架支持無生命週期的項目,能夠無縫渲染到門戶站點中來。這是我測試的截圖,你們有興趣能夠看下:

這是集成的 2018 年的 seeconf 官網:
thead&seeconf.gif

這是集成的口碑官網:
thead&koubei.gif

是否解決了 qiankun 的困境呢?

因爲通用邏輯和 layout 被分攤到一層層的「端應用」中,基座只須要作好對框架的引用和對規則的獲取與解析,就能驅動起總體的站點運行起來。子應用的路由也不用顧慮衝突,而考慮在主應用中 copy 一份。
基座能夠作到很薄,每一個端只需加載其須要的 UI 庫或組件。好比 pc 端使用了 antd,移動端可使用 antd-mobile,框架不會把 antd 加載到移動端去。

還有哪些不足?

  • 不建議基座具備路由系統,目前還沒去實驗基座路由和子應用路由衝突的場景。
  • 多路由系統衝突方案,暫時沒有支持 angular,對 vue 的支持可能存在一點兒 bug。
  • 多條依賴鏈場景暫未支持,目前只處理了第一條依賴鏈。
  • 子應用中一些 js 靈活寫法,如 eval 中的全局變量 var 去定義,包括自己的 var 定義的全局變量,還沒法正確掛載到虛擬 window 上。可能存在某些項目加載報錯的狀況。
  • 梳理了 21 種路由衝突的場景,可能存在例外的狀況。

瀏覽器兼容

主流瀏覽器 chrome, safari, edge, firefox 等都兼容,國內小衆瀏覽器 360、qq 瀏覽器等也兼容。優先使用 Proxy,不支持則退化爲 Object.defineProperty。

誰在使用

目前平頭哥 IoT 團隊在作芯片開放社區(OCC),這個框架的誕生也是爲了知足其不斷變化的業務需求。你們能夠看下 pc 端和移動端的 URL 是相同的,但激活的子應用是不一樣的。

也但願看到本文的同窗,在將來某個時刻使用 @icatjs/micro 框架,作出精彩紛呈的做品來!

應一些同窗的反饋,隨手搞了個示例 https//github.com/icatjs/icatjs-micro-demo, 請你們初步瞭解下該框架的使用。

相關文章
相關標籤/搜索