最快最简单的方法就是直接上iframe,天然实现的 CSS 隔离。但是由于隔离的太好了,导致iframe内的弹窗蒙层无法全屏,用户体验不好。于是就有其他实现CSS 隔离的方案。
CSS隔离方案
iframe隔离
scoped隔离,都是玩过VUE的,在style标签增加scoped后,就可以使得当前CSS只作用于当前组件(生成一段唯一编码),参考实现方式应该也可以在微前端实现。
自己在选择器上下功夫,比如子A子应用就在所有的选择器前增加 .componentsA,子应用都使用预处理器的话也好操作。
拿到激活的子应用样式后,删除失活的子应用样式(不适合一个页面有2个以上的子应用的场景,而且会影响父元素)。
Shadow DOM隔离,主流实现方式,下面细讲。
Shadow DOM
对于还不知道Shadow DOM是啥的哥们,你打开浏览器->开发者工具->设置(开发者工具的)->看下图
然后,恭喜你,哥们,你可以看到Shadow DOM了。你现在可以在Shadow DOM内看到很多原生DOM内部标签实现。
input-ShadowDOM
video-ShadowDOM
内部的样式与外面的样式是隔离的,这就是ShadowDOM,它不仅仅可以做到 DOM 元素的 CSS 样式隔离,还可以做到事件的隔离处理
示例
<body>
<template id="shadowTemplate">
<h1>Shadow DOM TEXT</h1>
<button onclick="ShadowButton('ShadowDOM button')">
Shadow DOM Button
</button>
<input onchange="ShadowInput(event)" value="Shadow DOM input" type="text">
</template>
<div id="shadow-outer">
<button onclick="notShadowClick(event)">我不是 Shadow DOM 中的</button>
<custom-element onclick="shadowTemplateClick(event)"></custom-element>
</div>
<script>
function ShadowButton(e) {
console.log("Shadow事件: ", e);
}
function ShadowInput(e) {
console.log("ShadowInput的值: ", e.target.value);
}
function shadowTemplateClick(e) {
console.log("shadowClick事件监听: ", e.target);
}
function notShadowClick(e) {
console.log("普通事件: ", e.target);
}
class CustomElement extends HTMLElement {
constructor() {
super();
// ShadowDOM的根节点
const shadowRoot = this.attachShadow({ mode: "open" });
// 获取挂载节点
const $template = document.getElementById("shadowTemplate");
// 挂载到根节点
shadowRoot.appendChild($template.content.cloneNode(true));
}
}
// 注册自定义元素
customElements.define("custom-element", CustomElement);
</script>
</body>
效果:
问题
ShadowDOM内部事件无法查看到内部触发事件的目标对象 Button 按钮,事件倒是正常,但是由于拿不到事件对象,那react开发者可就头疼了。都知道react(16,17以上该问题已不存在)一直是进行事件委托来处理事件,那么就会因为拿不到事件对象,而导致事件失效。
17+使用 React 挂载的 Root 节点进行事件委托,此时如果在 Shadow DOM 中使用 React 框架,那么事件可以被正确处理。具体原理请点击 -> click