微前端:實現「真正」的技術棧無關

爲何說「真正的技術棧無關」?那是由於目前不少解決方案宣稱的「技術棧無關」實現方式都不太理想,自古以來「技術棧無關」作得最好的,當屬 <iframe>。然而,大部分微前端方案都不約而同地放棄了 iframe 方案,文章 Why Not Iframe 做了說明,iframe 因爲良好的隔離性致使應用間的 UI 沒法完美融合,好比 iframe 中的彈窗遮罩沒法超越 iframe 的邊界,所以咱們不得不尋求其它解決方案。javascript

「技術棧無關」做爲微前端的核心價值之一,主要表現爲基座應用不限制接入應用的技術棧,每一個微應用都擁有獨立的運行時,可以避免 DOM、CSS、JS 受到外部的影響,或者對其它應用產生影響。html

DOM 隔離

不少解決方案並不關心 DOM 隔離問題,由於一般只要提供一個掛載節點,微應用即可以正常初始化。然而,微應用的 DOM 暴露在全局下,即可能被應用外的代碼獲取到,你沒法保證其餘人不搞點騷操做,或是本應用的代碼對外部產生非預期的影響。當前,能作到 DOM 隔離的技術,除了古老的 iframe 外,只有 Web components。前端

Web components 的一個重要屬性是封裝——能夠將標記結構、樣式和行爲隱藏起來,並與頁面上的其餘代碼相隔離,保證不一樣的部分不會混在一塊兒,可以使代碼更加乾淨、整潔。其中,Shadow DOM 接口是關鍵所在,它能夠將一個隱藏的、獨立的 DOM 附加到一個元素上。java

Web components 能夠維護一個隱藏的、獨立的 Shadow DOM,不被外部檢索到,從而達成 DOM 隔離的目的。git

CSS 隔離

樣式隔離是微前端方案解決的關鍵問題之一,有些方案經過合理的約定來規避 CSS 污染,常見的有 CSS Modules、BEM 規範、CSS-IN-JS 等,然而約定再好也擋不住開發者的很是規操做。並且,對於一個全新的項目,開發規範容易落實,對於現有的項目卻可能帶來大量的改造工做。其它方案如 qiankun 的「動態樣式表」,就算只跑單應用也存在與基座應用相互影響的可能。當下,真正作到 CSS 隔離只有 iframe 與 Web components。github

JS 隔離

JS 隔離是另外一關鍵問題,常見的處理手段是創造沙箱運行 JS 代碼。JS 沙箱可由 evalwithProxyFunction 等相互結合實現,其中,with 已經被廢棄,不適合使用。而 Proxy+evalProxy+Function 避免不了原型鏈污染等問題,而且劫持 <script> 加載的文件,再經過 eval 方式運行,會致使 deferasync 等特性失效。更重要的是,沒法經過 type="module" 使用原生 JS 模塊能力。因此,目前最佳的隔離手段還是 iframe。markdown

新一代解決方案

經過前面的分析可見 iframe 與 Web components 纔是理想的隔離技術,如果二者結合使用不就是一個絕佳的方案!新一代的微前端解決方案 <m-app> 即是經過整合同源 iframe 與 Web components 來實現技術棧無關,總體架構以下:架構

<m-app> 將微應用的 DOM 樹置於 Shadow DOM 中維護,從而實現 DOM 樹獨立以及 CSS 隔離,而 JavaScript 代碼則置於同源 iframe 中運行,由 iframe 提供獨立的運行環境。並經過劫持 `appendChild`、`replaceChild` 等有引入新元素能力的方法,分析處理其中的 `<script>` 元素,放置到 iframe 中運行。

此外,還劫持了 iframe 環境中的 document.querySelectordocument.addEventListener 等方法,將開發者的方法調用代理到 ShadowRoot 上去,保持原有開發方式不變,下降應用接入的改形成本。app

Object.defineProperties(document, {
    querySelector: {
        value: (...args) => ShadowRoot.querySelector(...args),
    },
    addEventListener: {
        value: (...args) => ShadowRoot.addEventListener(...args),
    },
});
複製代碼

並且,微應用的 Shadow DOM 與正常 DOM 的結構基本一致,總體看起來就像在用 <iframe> 同樣async

<m-app entry="http://example.com/path/to/entry.html"></m-app>
複製代碼
├─<iframe hidden>  
           │                 ├─<meta>
ShadowRoot─│        ├─<head>─├─<title>
           │        │        ├─...
           ├─<html>─│
                    │        ├─<h1>
                    ├─<body>─├─<div>
                             ├─...
複製代碼

最後,爲項目求一波 star ⭐⭐⭐

gitee.com/ambit/m-app

github.com/ambit-tsai/…

相關文章
相關標籤/搜索