qiankun类微前端内核解密(css沙箱隔离机制)

前言
qiankun模式是企业级后台迁移微前端的一个标杆模式,稍微有点技术的企业都会选择基于qiankun技术来自定义一套符合自身的微前端技术体系,所以这里面技术大部分是相通的,我司也不例外,在迁移微前端架构中有种种疑问,现在我们一起解密这个黑盒
qiankun类微前端内核实现主要功能
- JS沙箱:子应用之间互不影响,包括全局变量、事件等等
- CSS隔离:子应用之间样式互不影响
- Config Entry:配置每个子应用的JS和CSS
- 按需加载:切换到相应页面才加载对应资源
- 公共依赖加载:大部分子应用都需要用到的资源如何处理
- 预加载:空闲时间加载子应用资源
- 父子应用通信:子应用如何触发父级应用方法、父级应用如何调子级方法
单纯论微前端有多种方式,上面列表微前端核心功能也有多种实现方式,为了高效专注解密,本主题仅仅解密类qiankun微前端内核处理方式
CSS沙箱
shadowDOM
Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。
这里官方文档写的还是挺好的推荐直接看文档shadowDOM
简易示例
var shadowEl = document.querySelector(".shadow");
var shadow = shadowEl.attachShadow({mode: 'open'});
var link = document.createElement("a");
link.href = 'https://baidu.com';
link.innerHTML='百度'
shadow.appendChild(link);
特点
- 对主文档的
JavaScript
选择器隐身,比如querySelector
- 只使用
shadow tree
内部的样式,不使用主文档的样式
scopedCSS
核心原理:利用css属性选择器
层叠类似vue scoped
的方式做样式隔离,被隔离的应用的样式表会被特定规则改写成如下模式
div .react15-lib{
color: rgb(129, 143, 247);
}
div[data-qiankun="react15"] .react15-lib {
color: rgb(129, 143, 247);
}
那么qiankun
具体是如何实现的呢?它巧妙的利用CSSRule改写来实现上述功能。没用过这个功能的可能一脸懵逼,下面举个常用示例,可以找个网页打开F12
测试下
var style=document.createElement('style')
var textnode=document.createTextNode('#id{color:red}')
style.appendChild(textnode)
document.body.appendChild(style)
var styleList=document.querySelectorAll('style')
var current=styleList[styleList.length-1]
console.log(current.sheet)
不想试的可以直接看结果 undefined
我们可以通过这个接口便捷实现css的编辑
源码步骤如下
undefined
上图得知往createElement
传了四个参数appContent
子应用入口文件,strictStyleIsolation
是否开启严格的风格隔离,scopedCSS
是否开启scopedCss隔离,appName
子应用名称
export function isEnableScopedCSS(sandbox: FrameworkConfiguration['sandbox']) {
if (typeof sandbox !== 'object') {
return false;
}
if (sandbox.strictStyleIsolation) {
return false;
}
return !!sandbox.experimentalStyleIsolation;
}
通过上述源代码得知,传入的css隔离配置,shadowDOM
的css隔离优先于scopedCSS
,关键函数createElement
``` ts function createElement( appContent: string, strictStyleIsolation: boolean, scopedCSS: boolean, appName: string, ): HTMLElement { const containerElement = document.createElement('div'); containerElement.innerHTML = appContent; // appContent always wrapped with a singular div const appElement = containerElement.firstChild
- 一
- 二
- 三
- 四
- 五
- 六
- 七