引言
Ant Design V5 是截止目前 Ant Design 组件库最新版本,咱们在业务场景曾经开始利用,但在近期的测试中发现 Ant Design 组件无奈在 360 极速浏览器(极速模式)下显示组件款式。通过查看款式和查阅官网文档, Ant Design V5 组件的款式中大量应用了 :where() 选择器来升高选择器的优先级,以此缩小开发者降级组件库时额定调整自定义款式的老本。:where() 对于 Chrome 仅反对 Chrome 88 以上,截止目前 360 极速浏览器的 Chrome 版本为 86,因而无奈显示组件款式。
参考
- 浅谈CSS逻辑选择器 is、where、not、has
- :where() - MDN
对于此款式兼容问题,次要解决思路如下:
- 参照官网款式兼容文档,应用其提供的降级计划,移除掉 :where() 选择器。次要是通过 Context 为组件传入配置,敞开升高款式优先级的配置,以此移除 :where() 选择器。但针一些无奈接管 Context 的场景,例如 Message、Modal 和 Notification 等组件的动态调用模式,依然会应用 :where() 选择器。
- 对于第一点产生的问题,如果要在组件中应用 Message、Modal 和 Notification 组件,须要防止动态调用模式,采纳官网举荐的 Hook 调用模式,能够通过 App 包裹组件实现此操作。
- 但 Hook 调用模式只能在组件应用,业务场景上经常须要在非组件内调用相干,比拟典型就是在申请的响应拦截器中调用 Message 实现谬误提醒。对于在非组件调用能够通过类 Redux 数据全局共享与通信计划,在相干逻辑中(例如响应拦截器中)告诉组件调用 Message ,从而实现此需要。
通过降级计划移除 :where() 选择器
入口文件:
import ReactDOM from "react-dom/client";import {App} from "antd";import {StyleProvider, legacyLogicalPropertiesTransformer} from '@ant-design/cssinjs';import "./index.css";// 其余无关代码已省略...ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( <StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}> /* 敞开降权操作,移除 :where() 选择器 */ <App> /* Ant Design 提供的包裹组件,用于使得 Message、Modal、Notification 等组件的动态调用模式能获取到上下文,从而读取移除 :where() 选择器的配置 */ <RouterProvider router={router}/> </App> </StyleProvider>);
组件内动态调用 Message、Modal、Notification 等组件的形式:
import {FC, useEffect} from "react";import {App} from "antd";const Main: FC = () => { const {message, modal} = App.useApp(); useEffect(()=>{ message.success("胜利"); },[]) return ( <div> </div> );};export default Main;
通过数据通信机制解决非组件内调用 Message 等组件
通过 Redux 解决
以在 Axios 响应拦截器内应用 Message 组件实现谬误提醒为例,其余 Modal、Notification 组件应用形式同理。
依赖:react-redux、@reduxjs/toolkit
MessageAlert 组件 - 用于告诉此组件触发相干的音讯告诉:
import {useEffect} from "react";import {App} from "antd";import {useSelector} from "react-redux";import {RootState} from "@/store";function MessageAlert() { const {message: messageApi} = App.useApp(); const messageInfo = useSelector((state: RootState) => state.global.messageInfo); useEffect(() => { const {type, content} = messageInfo; if (messageApi && content) { switch (type) { case "success": messageApi.success(content); break; case "error": messageApi.error(content); break; case "warning": messageApi.warning(content); break; case "info": messageApi.info(content); break; default: messageApi.error(content); break; } } }, [messageApi, messageInfo]); return null;}export default MessageAlert;
入口文件:
import ReactDOM from "react-dom/client";import {App} from "antd";import {StyleProvider, legacyLogicalPropertiesTransformer} from '@ant-design/cssinjs';import MessageAlert from "@/components/MessageAlert";import "./index.css";// 其余无关代码已省略...ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( <StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}> /* 敞开降权操作,移除 :where() 选择器 */ <App> /* Ant Design 提供的包裹组件,用于使得 Message、Modal、Notification 等组件的动态调用模式能获取到上下文,从而顺利读取移除 :where() 选择器的配置 */ <RouterProvider router={router}/> <MessageAlert></MessageAlert> /* 音讯提醒组件,用于在非组件内通过数据通信形式实现调用。 */ </App> </StyleProvider>);
Redux Store 配置
store/global.ts
import {createSlice} from '@reduxjs/toolkit';import type {PayloadAction} from '@reduxjs/toolkit';export type MessageInfoType = { type: 'success' | 'error' | 'info' | 'warning'; content?: string;}export interface GlobalState { messageInfo: MessageInfoType;}const initialState: GlobalState = { messageInfo: { type: 'success', }};export const globalSlice = createSlice({ name: 'global', initialState, reducers: { saveMessageInfo: (state, action: PayloadAction<MessageInfoType>) => { state.messageInfo = {...action.payload}; } },});export const {saveMessageInfo} = globalSlice.actions;export default globalSlice.reducer;
store/index.ts
import {configureStore} from '@reduxjs/toolkit';import globalReducer from './global';export const store = configureStore({ reducer: { global: globalReducer, },});export type RootState = ReturnType<typeof store.getState>export type AppDispatch = typeof store.dispatch
申请拦截器内调用相干 action 实现音讯告诉:
import {store} from "@/store";// 其余无关代码已省略...axiosInstance.interceptors.response.use( async (response) => { const {data} = response; const {code, msg} = data; if (code !== 0) { // 谬误提醒 store.dispatch({ type: 'global/saveMessageInfo', payload: { type: 'error', content: msg || '服务端产生谬误' } }); } return response; }, // ...);export default axiosInstance;
不引入 Redux(自行实现小型状态管理器)
如果我的项目中不想额定引入 Redux,能够思考自行实现小型状态管理器。
store.ts - 实用于简略的数据共享场景的小型状态管理器
export type MessageInfoType = { type: 'success' | 'error' | 'info' | 'warning'; content?: string;}type State = { messageInfo: MessageInfoType;};type Listener = () => void;class Store { private state: State; // 用于存储状态 private listeners: Listener[]; // 用于存储所有组件对状态进行更新的函数 constructor() { this.state = {messageInfo: {type: 'success', content: ''}}; this.listeners = []; } // 获取状态 getState(): State { return this.state; } // 批改状态 setState(newState: State): void { this.state = newState; this.notify(); } // 订阅状态 subscribe(listener: Listener): void { this.listeners.push(listener); } // 勾销订阅 unsubscribe(listener: Listener): void { this.listeners = this.listeners.filter((l) => l !== listener); } // 用于告诉所有组件对状态进行更新 private notify(): void { for (const listener of this.listeners) { listener(); } }}const store = new Store();export {store};
MessageAlert 组件 - 应用小型状态管理器
import {useEffect, useState} from "react";import {App} from "antd";import {MessageInfoType, store} from '@/store/store';function MessageAlert() { const {message: messageApi} = App.useApp(); const [messageInfo, setMessageInfo] = useState<MessageInfoType>(store.getState().messageInfo); useEffect(() => { const handleMessageInfoChange = () => { setMessageInfo(store.getState().messageInfo); }; store.subscribe(handleMessageInfoChange); return () => { store.unsubscribe(handleMessageInfoChange); }; }, []); useEffect(() => { const {type, content} = messageInfo; if (messageApi && content) { switch (type) { case "success": messageApi.success(content); break; case "error": messageApi.error(content); break; case "warning": messageApi.warning(content); break; case "info": messageApi.info(content); break; default: messageApi.error(content); break; } } }, [messageApi, messageInfo]); return null;}export default MessageAlert;
申请拦截器配置
import {store} from "@/store.ts";// 其余无关代码已省略...axiosInstance.interceptors.response.use( async (response) => { const {data} = response; const {code, msg} = data; if (code !== 0) { // 谬误提醒 store.setState({messageInfo: {type: 'error', content: msg || '服务端产生谬误'}}); } return response; }, // ...);export default axiosInstance;