微前端——qiankun 主要特点

本文正在参加「金石计划 . 瓜分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

  1. 通过 url 请求到子应用的 index.html。
  2. 用正则匹配到其中的 js/css 相关标签,进行记录,然后删去。
  3. 删去 html/head/body 等标签。
  4. 返回 html 文本。

如何解析js

  1. 使用正则匹配 <script> 标签。
  2. 对于内联 js 的内容会直接记录到一个对象中。
  3. 对于外链 js 会使用 fetch 请求到内容,然后记录到这个对象中。
  4. 最后在加载子应用时直接把内容赋值在动态构建的 script 中。

如何解析css

  1. 正则匹配 <style><link> 标签。
  2. 内联 css (<style> 标签)的内容会直接记录到一个对象中。
  3. 外链 css (<link> 标签)则会使用 fetch 请到到内容(字符串),然后记录到这个对象中,执行时内容放到<style>标签,然后插入到页面,子项目卸载移除这些<style>标签,这样会把外链的 css 变成内联 css,切换子系统,不用重复请求,直接应用 css 样式,让子项目加载得更快。

2、css 隔离

css 隔离主要分为 2 种,一种是父子之间的隔离,另一种是子子之间的隔离。

子应用之间的隔离,qiankun 中并没有特别的提出,本质上就是在子应用加载时把其相应的样式加载进来,在卸载时进行移除即可。而父子之间的隔离在 qiankun 中有两种实现方法。

  • strictStyleIsolation: Shadow DOM

image.png

第一种是严格样式隔离,核心是 Shadow DOM。它可以让一个 dom 拥有自己的“影子” DOM 树,这个 DOM 树不能在主文档中被任意访问,可以拥有局部样式规则,天然实现了样式隔离,如上图所示,被代理的 dom节点称为 shadow host,shadow tree 中的根节点称为 shadow root。

比如我们常用的<video>标签,一个标签就实现了一个简易的播放器,但其实它是由一些看不到的 dom 封装而成的,这里就是使用了shadow DOM。

image.png

现在先来模拟一下父子的样式污染问题,在下面的 demo 中子应用的样式设置成所有字体颜色为红色,使得父元素和子元素所有的文字都为红色。

<div>        
    <h5>父元素</h5>    
</div>    
<div id="app1">        
    <style>            
        *{                
            color:red;            
        }
    </style>        
    <h5>子元素</h5>        
    <p class="title">一行文字</p>    
</div>

这样的结果就是子样式污染了父样式(效果如下图)。

image.png

使用严格样式隔离解决一下这个问题:获取到子应用的根节点,然后打开影子模式,把原来的 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 都在影子中,效果如下图所示,实现了父子之间的样式隔离。

image.png

  • 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>

image.png

这里首先获取到子应用,然后通过正则匹配其中的所有

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容