使用qiankun样式隔离element-ui弹框样式消失问题

2024-11-28  本文已影响0人  lihao_李浩

背景

我们在一个由主项目(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,  // 允许进一步修改或删除该属性
  });
}

解释

  1. afterMount 生命周期钩子
    在子应用加载之前,我们拦截了 getComputedStyleappendChild 方法,以确保 el-select 渲染的弹框能够正确插入到子应用的 shadow-root 中,并应用样式。

  2. getComputedStyle 重写
    getComputedStyle 用于获取元素的计算样式。由于 el-select 递归查找 body 元素,可能会遇到 shadow-root 元素,从而抛出错误。我们通过判断 node 是否为 ShadowRoot 实例来解决这个问题,确保获取到正确的样式。

  3. appendChild 方法拦截
    el-select 渲染弹框时会调用 document.body.appendChild,但由于样式隔离,我们将这个方法重新定义为插入到 shadow-root 中,确保弹框能够在正确的上下文中渲染并应用样式。


通过这个解决方案,我们成功地解决了样式隔离导致的 el-select 弹框样式丢失问题,并保证了子项目与主项目之间的样式隔离和正确显示。

上一篇 下一篇

猜你喜欢

热点阅读