本文正在参加「金石计划 . 瓜分6万现金大奖」
qiankun 是基于 single-spa 提出的微前端框架, 提供了更加开箱即用的 API(single-spa + sandbox + import-html-entry)。
Single-spa 是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架。
sandbox(沙箱)是将您的应用程序运行在一个隔离的空间里,以避免它们对其他程序和数据造成永久性的更改。
import-html-entry 用于获取子应用的 HTML 和 JS,同时对 HTML 和 JS 进行了各自的处理,以便于子应用在父应用中加载。
主要特点
1、HTML Entry 加载子应用
首先是子应用的加载方式与 single-spa 有明显的不同,single-spa 注册子应用本质上是 JS Entry,即通过从某一地址引入 js 文件来加载整个子应用。
singleSpa.registerApplication({
'appName',
() => System.import('appName'),
location => location.pathname.startsWith('appName'),
});
但是 qiankun 注册子应用的方式是通过一个 url,即使用 HTML Entry 的方式来引入子应用。
registerMicroApps([
{
name: 'react app',
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule'
},
]);
start();
**qiankun-子应用的加载
Html Entry 方法的主要步骤如下:首先通过 url 获取到整个 Html 文件,从 html 中解析出 html,js 和 css 文本,在主应用中创建容器,把 html 更新到容器中,然后动态创建 style 和 script 标签,把子应用的 css 和 js 赋值在其中,最后把容器放置在主应用中。
// 解析 HTML,获取 html,js,css 文本
const {htmlText, jsText, cssText} = importHTMLEntry('https://xxxx.com')
// 创建容器
const $= document.querySelector(container)
$container.innerHTML = htmlText
// 创建 style 和 js 标签
const $style = createElement('style', cssText)
const $script = createElement('script', jsText)
$container.appendChild([$style, $script])
如何解析html?
- 通过 url 请求到子应用的 index.html。
- 用正则匹配到其中的 js/css 相关标签,进行记录,然后删去。
- 删去 html/head/body 等标签。
- 返回 html 文本。
如何解析js?
- 使用正则匹配
<script>
标签。 - 对于内联 js 的内容会直接记录到一个对象中。
- 对于外链 js 会使用 fetch 请求到内容,然后记录到这个对象中。
- 最后在加载子应用时直接把内容赋值在动态构建的 script 中。
如何解析css?
- 正则匹配
<style><link>
标签。 - 内联 css (
<style>
标签)的内容会直接记录到一个对象中。 - 外链 css (
<link>
标签)则会使用 fetch 请到到内容(字符串),然后记录到这个对象中,执行时内容放到<style>
标签,然后插入到页面,子项目卸载移除这些<style>
标签,这样会把外链的 css 变成内联 css,切换子系统,不用重复请求,直接应用 css 样式,让子项目加载得更快。
2、css 隔离
css 隔离主要分为 2 种,一种是父子之间的隔离,另一种是子子之间的隔离。
子应用之间的隔离,qiankun 中并没有特别的提出,本质上就是在子应用加载时把其相应的样式加载进来,在卸载时进行移除即可。而父子之间的隔离在 qiankun 中有两种实现方法。
- strictStyleIsolation: Shadow DOM
第一种是严格样式隔离,核心是 Shadow DOM。它可以让一个 dom 拥有自己的“影子” DOM 树,这个 DOM 树不能在主文档中被任意访问,可以拥有局部样式规则,天然实现了样式隔离,如上图所示,被代理的 dom节点称为 shadow host,shadow tree 中的根节点称为 shadow root。
比如我们常用的<video>
标签,一个标签就实现了一个简易的播放器,但其实它是由一些看不到的 dom 封装而成的,这里就是使用了shadow DOM。
现在先来模拟一下父子的样式污染问题,在下面的 demo 中子应用的样式设置成所有字体颜色为红色,使得父元素和子元素所有的文字都为红色。
<div>
<h5>父元素</h5>
</div>
<div id="app1">
<style>
*{
color:red;
}
</style>
<h5>子元素</h5>
<p class="title">一行文字</p>
</div>
这样的结果就是子样式污染了父样式(效果如下图)。
使用严格样式隔离解决一下这个问题:获取到子应用的根节点,然后打开影子模式,把原来的 dom 结构赋值到代理的影子根节点中,然后清空原来的 dom 结构。
function openShadow(domNode) {
var shadow = domNode.attachShadow({ mode: "open" });
shadow.innerHTML = domNode.innerHTML;
domNode.innerHTML = "";
}
var bodyNode = document.getElementById("app1");
openShadow(bodyNode);
现在可以在 dom 树中看到,原来的子应用以及开启了影子模式,其中的子 dom 都在影子中,效果如下图所示,实现了父子之间的样式隔离。
- experimentalStyleIsolation
第二种父子样式隔离是实验性样式隔离 ,即通过运行时修改 CSS 选择器来实现子应用间的样式隔离。
下面也是一个模拟污染的 demo,可以看到主应用和子应用有重名的选择器,子应用在后面,所以父样式被覆盖,造成了污染。
<head>
<style>
p.title {
color:red;
}
</style>
</head>
<body>
<p class="title">父应用</p>
<div id="data-qiankun-A">
<style>
p.title {
color:blue;
}
</style>
<p class="title">子应用</p>
</div>
</body>
这里首先获取到子应用,然后通过正则匹配其中的所有
暂无评论内容