爲何說「真正的技術棧無關」?那是由於目前不少解決方案宣稱的「技術棧無關」實現方式都不太理想,自古以來「技術棧無關」作得最好的,當屬 <iframe>
。然而,大部分微前端方案都不約而同地放棄了 iframe 方案,文章 Why Not Iframe 做了說明,iframe 因爲良好的隔離性致使應用間的 UI 沒法完美融合,好比 iframe 中的彈窗遮罩沒法超越 iframe 的邊界,所以咱們不得不尋求其它解決方案。javascript
「技術棧無關」做爲微前端的核心價值之一,主要表現爲基座應用不限制接入應用的技術棧,每一個微應用都擁有獨立的運行時,可以避免 DOM、CSS、JS 受到外部的影響,或者對其它應用產生影響。html
不少解決方案並不關心 DOM 隔離問題,由於一般只要提供一個掛載節點,微應用即可以正常初始化。然而,微應用的 DOM 暴露在全局下,即可能被應用外的代碼獲取到,你沒法保證其餘人不搞點騷操做,或是本應用的代碼對外部產生非預期的影響。當前,能作到 DOM 隔離的技術,除了古老的 iframe 外,只有 Web components。前端
Web components 的一個重要屬性是封裝——能夠將標記結構、樣式和行爲隱藏起來,並與頁面上的其餘代碼相隔離,保證不一樣的部分不會混在一塊兒,可以使代碼更加乾淨、整潔。其中,Shadow DOM 接口是關鍵所在,它能夠將一個隱藏的、獨立的 DOM 附加到一個元素上。java
Web components 能夠維護一個隱藏的、獨立的 Shadow DOM,不被外部檢索到,從而達成 DOM 隔離的目的。git
樣式隔離是微前端方案解決的關鍵問題之一,有些方案經過合理的約定來規避 CSS 污染,常見的有 CSS Modules、BEM 規範、CSS-IN-JS 等,然而約定再好也擋不住開發者的很是規操做。並且,對於一個全新的項目,開發規範容易落實,對於現有的項目卻可能帶來大量的改造工做。其它方案如 qiankun 的「動態樣式表」,就算只跑單應用也存在與基座應用相互影響的可能。當下,真正作到 CSS 隔離只有 iframe 與 Web components。github
JS 隔離是另外一關鍵問題,常見的處理手段是創造沙箱運行 JS 代碼。JS 沙箱可由 eval
、with
、Proxy
、Function
等相互結合實現,其中,with
已經被廢棄,不適合使用。而 Proxy
+eval
或 Proxy
+Function
避免不了原型鏈污染等問題,而且劫持 <script>
加載的文件,再經過 eval
方式運行,會致使 defer
、async
等特性失效。更重要的是,沒法經過 type="module"
使用原生 JS 模塊能力。因此,目前最佳的隔離手段還是 iframe。markdown
經過前面的分析可見 iframe 與 Web components 纔是理想的隔離技術,如果二者結合使用不就是一個絕佳的方案!新一代的微前端解決方案 <m-app> 即是經過整合同源 iframe 與 Web components 來實現技術棧無關,總體架構以下:架構
此外,還劫持了 iframe 環境中的 document.querySelector
、 document.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 ⭐⭐⭐