背景
我们在一个由主项目(Vue 3)和子项目(Vue 2)组成的系统中,添加了样式隔离的功能。主项目和子项目通过 qiankun
实现微前端架构。
问题
子项目中的 el-select
下拉框在渲染时,渲染到了主项目的 body
元素中。由于样式隔离的机制,弹框在主项目中没有样式应用,导致显示异常。
解决思路
el-select
渲染下拉框时会调用 document.body.appendChild
方法。为了解决样式丢失的问题,我们在主应用中对该方法进行了拦截。当需要将元素插入到 body
时,我们将其插入到 shadow-root
中,从而确保样式能够正确应用。
此外,由于 el
在渲染弹框时会递归向上查找 body
元素,若查找到的是 shadow-root
,会抛出错误,提示 "不是一个 Element 对象"。因此,我们还对 getComputedStyle
方法进行了重写,以解决这一问题。
解决方案代码
// qiankun 的生命周期函数
async afterMount(app, global) {
console.log('app', app);
// 获取子应用的 shadowRoot
const shadowRoot = document.querySelector(`[data-name="${app.name}"]`)?.shadowRoot
// 保存原始的 appendChild 方法
const originAppendChild = global.document.body.appendChild;
// 保存原始的 getComputedStyle 方法
const originGetComputedStyle = global.window.getComputedStyle;
// 重写 getComputedStyle 方法,解决 shadow-root 中的样式问题
Object.defineProperty(global.window, 'getComputedStyle', {
value(node: Element) {
// 如果是 shadowRoot 元素,获取其子元素的计算样式
if (node instanceof ShadowRoot) {
const shadowBody = node.querySelector('div') || document.body;
return originGetComputedStyle.call(this, shadowBody);
} else {
// 否则,调用原始的 getComputedStyle 方法
return originGetComputedStyle.call(this, node);
}
},
});
// 重新定义 appendChild 方法,插入到 shadowRoot 中
Object.defineProperty(global.document.body, 'appendChild', {
value(node: Node) {
// 如果 shadowRoot 存在,将元素插入到 shadowRoot 内
if (shadowRoot) {
return shadowRoot.querySelector('div')?.appendChild(node);
}
// 否则,调用原始的 appendChild 方法
return originAppendChild.call(this, node);
},
writable: true, // 确保 appendChild 方法是可写的
configurable: true, // 允许进一步修改或删除该属性
});
}
解释
-
afterMount
生命周期钩子:
在子应用加载之前,我们拦截了getComputedStyle
和appendChild
方法,以确保el-select
渲染的弹框能够正确插入到子应用的shadow-root
中,并应用样式。 -
getComputedStyle
重写:
getComputedStyle
用于获取元素的计算样式。由于el-select
递归查找body
元素,可能会遇到shadow-root
元素,从而抛出错误。我们通过判断node
是否为ShadowRoot
实例来解决这个问题,确保获取到正确的样式。 -
appendChild
方法拦截:
el-select
渲染弹框时会调用document.body.appendChild
,但由于样式隔离,我们将这个方法重新定义为插入到shadow-root
中,确保弹框能够在正确的上下文中渲染并应用样式。
通过这个解决方案,我们成功地解决了样式隔离导致的 el-select
弹框样式丢失问题,并保证了子项目与主项目之间的样式隔离和正确显示。
网友评论