前言
最近在从antd3升级到antd5到时候,遇到一个问题,有客户报过来在部分低版本360浏览器会出现antd到message全局提示无法自动关闭的问题,这里记录一下解决的过程
解决过程
首先我们想到的是不是兼容问题导致的,antd5有使用:where选择器来降低选择器的优先级,但是:where在低版本浏览器的兼容性较差,antd官方建议可以使用 @ant-design/cssinjs
取消默认的降权操作
jsx
import { StyleProvider } from '@ant-design/cssinjs';
// `hashPriority` 默认为 `low`,配置为 `high` 后,
// 会移除 `:where` 选择器封装
export default () => (
<StyleProvider hashPriority="high">
<MyApp />
</StyleProvider>
);
切换后,样式将从 :where
切换为类选择器:
jsx
-- :where(.css-bAMboO).ant-btn {
++ .css-bAMboO.ant-btn {
color: #fff;
}
可是这个配置一开始改造的时候我们已经加了,为什么还是不行呢,难道不是这个问题导致的?
然后我们又看了message的文档,发现现在已经不推荐用静态方法的方式来调用,推荐是通过hook的,我们试了一下,通过hook的方式,在360浏览器就正常了。
但是原来的调用在class,hook,service层都有调用,不可能全部改成hook,因为客户叫的也比较凶,我们想到了一个临时方案来解决
首先我们定义了一个MessageHolder组件,这个组件在最顶层引入,组件里面通过eventEmitter监听
消息,收到消息会通过meaageApi调用message组件
MessageHolder.jsx
jsx
import React, { useEffect } from'react';
import { message } from'antd';
import ee from"./eventEmitter";
functionMessageHolder(){
const [messageApi, contextHolder] = message.useMessage();
useEffect(() => {
const messageTypes = ['warning','success','error','info'];
messageTypes.forEach((m) => {
ee.on(m, (data) => {
messageApi[m](data.content);
});
})
}, []);
return <>
{contextHolder}
</>;
}
exportdefault MessageHolder;
eventEmitter.js
jsx
import EventEmitter from "events";
if(!window.ee) {
window.ee =new EventEmitter();
}
exportdefault window.ee;
最后我们实现了一个message.js来兼容原来的message api,这样我们只要改一下引用,调用的地方就不用改了
message.js
jsx
import ee from"./eventEmitter";
exportdefault {
warning: (content) => {
ee.emit('warning', { content });
},
error: (content) => {
ee.emit('error', { content });
},
success: (content) => {
ee.emit('success', { content });
},
info: (content) => {
ee.emit('info', { content });
}
}
这个方式暂时解决了低版本360浏览器的问题,但是问题的原因还是没有找到。
接下来我就开始一步一步调试antd的源码,发现静态方法方式调用message的api其实antd内部是通过render单独渲染的,所以是拿不到我项目里面定义的context的, 这个配置也就没有传下去
那针对这种静态方法调用我怎么把context传下去呢?最后我在github的issue里面找到了答案。antd 5.13.0 以上版本提供了一个配置,这个配置只对静态方法调用生效,通过传递hoderRender可以指定渲染的context
OK通过下面的方式就可以把StyleProvider传下去了,360浏览器里面表现也正常了。
jsx
ConfigProvider.config({
holderRender: (children) => {
return <StyleProvider hashPriority="high">
<ConfigProvider>
{children}
</ConfigProvider>
</StyleProvider>
},
});