![](https://img.haomeiwen.com/i10131721/a42a92973bf25852.jpg)
- 确保单实例场景子应用之间的样式隔离,但是无法确保主应用跟子应用、或者多实例场景的子应用样式隔离
start({sandbox : true})//沙箱隔离,
判断沙箱隔离的方法,发现直接设置sandbox=true是不行的,需要使用experimentalStyleIsolation
这个属性配置。
export function isEnableScopedCSS(sandbox: FrameworkConfiguration['sandbox']) {
if (typeof sandbox !== 'object') {
return false;
}
if (sandbox.strictStyleIsolation) {
return false;
}
return !!sandbox.experimentalStyleIsolation;
}
-
{ strictStyleIsolation: true }
开启严格的样式隔离模式 ,shawod dom'
start({shadow:{ strictStyleIsolation: true }})
直观的可以看到,子应用的代码不是正常的dom,上面显示的shadow-root,先不管shadow-root是什么,接着往下看
![](https://img.haomeiwen.com/i10131721/407c85abfec1402d.png)
-
experimentalStyleIsolation
给子应用加上自定义的data-xxx属性,相当于vue中我们通常使用的scoped,从生成的代码中可以看出来
1668608526445.png
在主应用中,通过配置sandbox
做到样式隔离
start({ sandbox: { strictStyleIsolation: true } });
在start得时候配置得参数,但是只有等到start函数里面 frameworkStartedDefer.resolve()
之后才能往下执行.
registerMicroApps
只是准备好注册的应用配置信息,只有等调用了start方法之后,才会走registerApplication
相关逻辑,registerApplication
是single-spa
中的一个主要的方法,乾坤实现微应用也主要用的是single-spa
这个库
start 方法
export function start(opts: FrameworkConfiguration = {}) {
frameworkConfiguration = { prefetch: true, singular: true, sandbox: true, ...opts };
startSingleSpa({ urlRerouteOnly });
started = true;
//执行loadApp相关逻辑
frameworkStartedDefer.resolve();
}
修改appid得代码是在single-spa中还是qiankun中得?->qiankun,在处理模板的时候使用shadow dom
实现的
loadApp中使用严格样式隔离的代码
export async function loadApp<T extends ObjectType>(
app: LoadableApp<T>,
configuration: FrameworkConfiguration = {},
lifeCycles?: FrameworkLifeCycles<T>,
): Promise<ParcelConfigObjectGetter> {
const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation;
let initialAppWrapperElement: HTMLElement | null = createElement(
appContent,
strictStyleIsolation,
scopedCSS,
appInstanceId,
);
}
createElement方法相关代码,我们在start
方法中配置了strictStyleIsolation
严格样式模式,因此会走到strictStyleIsolation
判断相关逻辑里面,从这里面就可以看到创建shadow dom
相关代码
function createElement(
appContent: string,
strictStyleIsolation: boolean,
scopedCSS: boolean,
appInstanceId: string,
): HTMLElement {
const containerElement = document.createElement('div');
containerElement.innerHTML = appContent;
// appContent always wrapped with a singular div
const appElement = containerElement.firstChild as HTMLElement;
//绝对样式隔离相关代码
if (strictStyleIsolation) {
//使用shadow做样式隔离
const { innerHTML } = appElement;
appElement.innerHTML = '';
let shadow: ShadowRoot;
if (appElement.attachShadow) {
shadow = appElement.attachShadow({ mode: 'open' });
} else {
// createShadowRoot was proposed in initial spec, which has then been deprecated
shadow = (appElement as any).createShadowRoot();
}
shadow.innerHTML = innerHTML;
}
//...scopedCSS判断相关代码
return appElement;
}
使用scopedCss
做到样式隔离
前面入口出的逻辑和shadow
大致相同,scopedCss代码也很好理解,就是先查找要添加的应用是否有相应的属性,没有的话给子应用容器添加一个属性。然后遍历每个style标签,在每个style标签代码前面增加属性选择符,通过属性选择符+样式来确保样式不被重写
if (scopedCSS) {
const attr = appElement.getAttribute(css.QiankunCSSRewriteAttr);
if (!attr) {
//这边attr生成的属性:data-qiankun :vue2App
appElement.setAttribute(css.QiankunCSSRewriteAttr, appInstanceId);
}
const styleNodes = appElement.querySelectorAll('style') || [];
forEach(styleNodes, (stylesheetElement: HTMLStyleElement) => {
css.process(appElement!, stylesheetElement, appInstanceId);
});
}
css.process方法
export const process = (
appWrapper: HTMLElement,
stylesheetElement: HTMLStyleElement | HTMLLinkElement,
appName: string,
): void => {
// lazy singleton pattern
if (!processor) {
processor = new ScopedCSS();
}
const mountDOM = appWrapper;
if (!mountDOM) {
return;
}
const tag = (mountDOM.tagName || '').toLowerCase();
if (tag && stylesheetElement.tagName === 'STYLE') {
//生成前缀
const prefix = `${tag}[${QiankunCSSRewriteAttr}="${appName}"]`;
processor.process(stylesheetElement, prefix);
}
};
可以将标记结构,样式和行为隐藏起来,和页面上其他代码相隔离。
shadow dom的一个demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
h1 {
color: red;
}
</style>
<body>
<h1>hello 这个主页中的内容</h1>
<div id="box"></div>
</body>
<script>
window.onload = function () {
let box = document.getElementById("box");
//开启shadow dom
box.attachShadow({ mode: "open" });
let shadow = box.shadowRoot;
var h1 = document.createElement("h1");
h1.innerHTML = "world 这是shaow dom中的内容";
var style = document.createElement("style");
style.textContent = `h1{color:green}`;
shadow.appendChild(style);
shadow.appendChild(h1);
};
</script>
</html>
![](https://img.haomeiwen.com/i10131721/7da26a004380e37f.png)
乾坤框架系列学习
01.学习微前端架构-乾坤
02.资源加载过程分析
03.乾坤css加载机制
04.乾坤js隔离机制
乾坤沙箱实现原理
网友评论