qiankun css隔离导致antd样式失效问题

1,255 阅读4分钟

前言

在qiankun中,提供了2种css样式隔离的配置,在这里先简单介绍一下它们的原理

  • experimentalStyleIsolation

    • 原理: 使用动态样式前缀来实现样式隔离。

    • 具体实现:

    • 为每个微应用的样式添加一个唯一的前缀。

    • 动态修改微应用的样式表,将选择器添加前缀。

    • 为微应用的根元素添加相应的前缀类名。

  • strictStyleIsolation

    • 原理:使用 Shadow DOM 来实现严格的样式隔离。
    • 具体实现:
    • 为每个微应用创建一个 Shadow DOM。
    • 将微应用的内容渲染到这个 Shadow DOM 中。
    • Shadow DOM 提供了一个独立的 DOM 树和样式作用域。

由于qiankun官方提示将在3.0移除strictStyleIsolation,所以我使用了experimentalStyleIsolation方案

但当qiankun开启experimentalStyleIsolation方式的样式隔离时,会碰到antd组件库全局样式丢失的问题,包括但不限于button、select等组件的样式丢失、失效问题,在各大网站搜索无果,所以自行探索了一下,做一波分享。

本文相关技术栈:

Antd@5.19

qiankun@2.10

antd全局样式丢失问题

现象

在基座应用中 / 独立运行时

问题定位

当我们检查基座应用中的style时,可以发现一个问题:

上图:antd的全局样式被覆盖,下图:qiankun的experimentalStyleIsolation隔离样式(罪魁祸首)

解决方案

antd5.x后,加入了:where选择器来降低antd全局样式的css权重。

Ant Design 的 CSS-in-JS 默认通过 :where 选择器降低 CSS Selector 优先级,以减少用户升级时额外调整自定义样式的成本,不过 :where 语法的兼容性在低版本浏览器比较差 --Ant Design官网 v5.x

所以,我们只需要取消:where选择题,提高antd的css样式权重,就可以将样式恢复正常。但怎么做呢?正好AntD提供了一个官方的API:StyleProvider

import { StyleProvider } from '@ant-design/cssinjs';
// `hashPriority` 默认为 `low`,配置为 `high` 后,
// 会移除 `:where` 选择器封装
export default () => (
  <StyleProvider hashPriority="high">
    <MyApp />
  </StyleProvider>
);

这个API的本意是提供一个5.x版本兼容旧版本浏览器的方法,但实现的原理是将:where选择器移除,用class选择器代替。正好可以满足我们的使用场景,提升antd全局样式的权重。

加上后:

风险:

让antd的全局样式突破experimentalStyleIsolation的隔离,可能会污染基座应用的样式。

(推测场景:基座应用使用antd、微应用使用antd,且它们使用了不一样的主题)

如果你发生了这样的情况,可以尝试使用 antd的 <ConfigProvider prefixCls="custom-">提供一个custom前缀,避免这个情况。

到这里为止,看起来已经解决了样式问题,但当我打开微应用中的Modal组件时,又出现了新的问题:

Antd的Modal组件样式丢失 / 挂载位置错误问题

现象

上图为基座应用中加载微应用,下图为独立运行时

下图为在基座运行时的另一个页面,modal出现在页面的下方

image.png

问题定位

当我们检查基座应用中的dom层级时,可以发现一个问题

默认情况下,Ant Design 的 Modal 组件会被渲染到 document.body。(原理是modal内部实现时使用了React.createPortal ) 。

这意味着 Modal 的 DOM 结构会被移到微应用的 Shadow DOM 或样式隔离容器之外。 (对应strictStyleIsolation / experimentalStyleIsolation 的隔离方案),导致了样式的丢失

解决方案

使用Modal的API - getContainer

<Modal {...props} getContainer={false} />
// 指定 Modal 挂载的节点,但依旧为全屏展示,false 为挂载在当前位置
// 如果一个个写太麻烦,可以封装这样一个组件,后续都使用该组件而不是antd的modal

使用后,modal挂载的位置在样式隔离容器 / shadowDom内部 , modal样式丢失的问题得到解决,因为dom的层级太深,这里不再贴图了。

可能会有人说在Modal中一个个加太麻烦了,为什么不用Antd提供的ConfigProvider

<ConfigProvider
  getPopupContainer={node => {
    if (node) {
      return node.parentNode;
   }
    return document.body;
  }}
 >
   <App />
 </ConfigProvider>

我尝试过,但console出getPopupContainer函数传递的node不是我想要的目标node,且样式问题也仍然存在,了解原理的同学欢迎评论指正