关于前端:解决-Ant-Design-V5-样式兼容问题

3次阅读

共计 5989 个字符,预计需要花费 15 分钟才能阅读完成。

引言

Ant Design V5 是截止目前 Ant Design 组件库最新版本,咱们在业务场景曾经开始利用,但在近期的测试中发现 Ant Design 组件无奈在 360 极速浏览器(极速模式)下显示组件款式。通过查看款式和查阅官网文档,Ant Design V5 组件的款式中大量应用了 :where() 选择器来升高选择器的优先级,以此缩小开发者降级组件库时额定调整自定义款式的老本。:where() 对于 Chrome 仅反对 Chrome 88 以上,截止目前 360 极速浏览器的 Chrome 版本为 86,因而无奈显示组件款式。

参考

  1. 浅谈 CSS 逻辑选择器 is、where、not、has
  2. :where() – MDN

对于此款式兼容问题,次要解决思路如下:

  1. 参照官网款式兼容文档,应用其提供的降级计划,移除掉 :where() 选择器。次要是通过 Context 为组件传入配置,敞开升高款式优先级的配置,以此移除 :where() 选择器。但针一些无奈接管 Context 的场景,例如 Message、Modal 和 Notification 等组件的动态调用模式,依然会应用 :where() 选择器。
  2. 对于第一点产生的问题,如果要在组件中应用 Message、Modal 和 Notification 组件,须要防止动态调用模式,采纳官网举荐的 Hook 调用模式,能够通过 App 包裹组件实现此操作。
  3. 但 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;
正文完
 0