微前端:实现“真正”的技术栈无关

为何说“真正的技术栈无关”?那是由于目前不少解决方案宣称的“技术栈无关”实现方式都不太理想,自古以来“技术栈无关”作得最好的,当属 <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/…

相关文章
相关标签/搜索