news 2026/9/8 20:22:22

Ant Design Empty 语义化结构定制:classNames 与 styles 对象/函数用法全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ant Design Empty 语义化结构定制:classNames 与 styles 对象/函数用法全解析

Ant Design Empty 语义化结构定制:classNames 与 styles 对象/函数用法全解析

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

Empty(空状态)组件是业务页面中「暂无数据」场景的标配。antd v5.23.0 起,Empty 引入了「语义化结构(Semantic DOM)」定制能力:通过classNamesstyles两个属性,并支持「对象」与「函数」两种写法,即可对根节点、图片、描述、底部操作区分别做样式定制。本文以仓库中 style-class 示例 及其配套 style-class.md 说明文档为主体,结合组件源码与测试用例,完整讲解这套定制 API 的结构划分、两种写法、底层合并原理与可验证的测试行为。

Empty 的语义化结构:四个可定制节点

在动手写样式前,先明确「往哪写」。查看 Empty 组件实现 中定义的EmptySemanticType,Empty 的语义化结构一共包含四个节点:

export type EmptySemanticType = { classNames?: { root?: string; image?: string; description?: string; footer?: string; }; styles?: { root?: React.CSSProperties; image?: React.CSSProperties; description?: React.CSSProperties; footer?: React.CSSProperties; }; };

这四个节点对应的实际 DOM 及默认 class 前缀(默认prefixClsant-empty)如下,与渲染代码一一对应:

语义化 key对应元素渲染位置说明(来自 Semantic DOM 交互预览)
root最外层容器div.ant-empty根元素控制文本对齐、字体、行高与整体布局
image图片容器div.ant-empty-image图片节点控制高度、透明度、边距与图片样式
description描述div.ant-empty-description描述节点控制描述文案颜色等
footer底部操作区div.ant-empty-footerfooter 节点控制与描述的上边距以及操作按钮(如children传入的 Button)

同时注意两点容易忽略的实现细节:

  • image使用内置的Empty.PRESENTED_IMAGE_SIMPLE(简单版小图)时,根元素会额外追加ant-empty-normalclass(见 className 拼接逻辑),可据此针对性区分默认图与简单图的样式。
  • 旧的imageStyle属性已标记为@deprecated,组件会在开发环境输出imageStylestyles.image的废弃警告(见 deprecated 检测),新代码请统一走styles.image

对象写法:静态为每个语义节点单独定制

在 style-class.tsx 示例中,stylesObject使用「对象」写法,为不同节点一次性注入行内样式:

const stylesObject: EmptyProps['styles'] = { root: { backgroundColor: '#f5f5f5', borderRadius: '8px' }, image: { filter: 'grayscale(100%)' }, description: { color: '#1890ff', fontWeight: 'bold' }, footer: { marginTop: '16px' }, };
  • 每个 key 对应上文四个语义节点之一,value 是标准的React.CSSProperties
  • root的效果会落到根div.ant-emptystyle上,image/description/footer同理;
  • styles是行内样式,天然具备最高优先级,适合不需要抽离成样式文件的小范围调整。

classNames的对象写法则用于挂自定义 class(便于结合 CSS Modules、Tailwind 或普通全局样式表),示例中先借助antd-stylecreateStaticStyles生成带css片段的静态 class,再挂载到根节点:

import { createStaticStyles } from 'antd-style'; const classNames = createStaticStyles(({ css }) => ({ root: css` border: 1px dashed #ccc; padding: 16px; `, })); // 组件内使用 const emptyClassNames: EmptyProps['classNames'] = { root: classNames.root, }; <Empty {...emptySharedProps} description="Object styles" classNames={emptyClassNames} styles={stylesObject} />

说明:示例中使用的createStaticStyles来自antd-style(已列入仓库 package.json 依赖),用于把 CSS-in-JS 片断编译为一份可复用的静态 class;若你的项目不使用antd-style,直接在classNames.root传入自己定义的 className 字符串即可,API 语义完全一致。

示例还通过emptySharedProps复用了两个渲染实例的公共配置——image采用Empty.PRESENTED_IMAGE_SIMPLEchildren传入主操作按钮Create Now——这正好演示了「语义化定制」与「内容定制」可以正交组合。

函数写法:根据 props 动态返回样式

对象写法的局限是无法感知当前组件的实际 props。因此classNames/styles都支持「函数」写法:函数接收{ props }参数(即当前 Empty 接收到的完整 props),返回同样结构的对象。示例中的stylesFn根据是否有description动态切换配色:

const stylesFn: EmptyProps['styles'] = ({ props }): GetProp<EmptyProps, 'styles', 'Return'> => { if (props.description) { return { root: { backgroundColor: '#e6f7ff', border: '1px solid #91d5ff' }, description: { color: '#1890ff', fontWeight: 'bold' }, image: { filter: 'hue-rotate(180deg)' }, }; } return {}; };

写法要点:

  • 入参为{ props }props即传给<Empty>的全部属性(descriptionimagechildren等皆可参与判断);
  • 返回值类型与对象写法相同,且可以不写全所有节点(上例无description时返回空对象,即「不加额外样式」);
  • 类型上可用EmptyProps['styles']标注,若需精确定位函数形式返回值,可用GetProp<EmptyProps, 'styles', 'Return'>(仓库示例即采用此写法,这也是GetProp泛型工具在 v5 语义化 API 中的典型用法)。

在组件内,函数写法与对象写法可混用:classNames用对象(挂静态 class),styles用函数(动态行内样式),如示例所示。

底层原理:多来源合并与优先级

无论对象还是函数,最终都会汇入统一合并逻辑。Empty 内部调用了通用 Hook useMergeSemantic:

const [mergedClassNames, mergedStyles] = useMergeSemantic< EmptySemanticAllType['classNames'], EmptySemanticAllType['styles'], EmptyProps >([contextClassNames, classNames], [contextStyles, contextStyleRoot, styles, styleRoot], { props, });

从这段实现可以得出四个关键结论:

  1. 函数先求值再合并resolveStyleOrClass会对函数形式执行value({ props }),得到结果对象后再参与合并(实现代码)。
  2. 支持来自ConfigProvider的全局配置contextClassNames/contextStyles/contextStyle通过 useComponentConfig('empty') 取自 ConfigProvider 的empty组件级配置,因此可在应用根部统一定义全局 Empty 风格,再被组件局部配置覆盖。
  3. 合并顺序决定优先级:styles 按传入顺序用{ ...acc, ...cur }浅合并,后传入的键值覆盖前者(mergeStyles);classNames 则由clsx拼接共存。
  4. 行内 style 与全局 style 的差异被妥善处理style/ 上下文style会被useSemanticRootStyle包裹为{ root: style }再参与合并,保证root节点的styles.root覆盖关系符合「组件内联 > ConfigProvider」的直觉。

「root 样式优先级」这一点有专门测试背书:semantic.test.tsx 中的 root style priority 用例 通过ConfigProvider empty={{ styles, style }}与组件自身styles/style组合断言根节点最终样式,可看作该优先级的可执行规范。

测试用例佐证:函数化定制的两种形态

仓库中 semantic.test.tsx 对本示例所展示的两种写法做了直接验证:

  • 函数化动态切换classNames/styles以函数传入时,测试断言带description时根节点挂上.empty-with-desc且背景为红,移除description后 rerender 切换为.empty-no-desc且背景为蓝——证明函数每次渲染都会基于最新 props 重新求值;
  • 对象形式生效{ root: 'empty-custom', image: 'empty-image-custom' }{ root: {...}, image: {...} }会被正确写到对应语义节点,通过container.querySelector('.empty-custom').empty-image-custom断言。

同时,API 文档 中classNames/styles的类型定义也与此一一对应:

Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> // classNames Record<SemanticDOM, CSSProperties> | (info: { props }) => Record<SemanticDOM, CSSProperties> // styles

两者自5.23.0版本引入,并且均可通过 ConfigProvider 的组件级全局配置 下发,实现全站空状态风格统一。

实践建议

  • 静态需求用对象,条件需求用函数:仅需固定美化时对象写法更清晰;需要「有/无描述」「是否简单图」「是否 RTL」等条件分支时,用函数写法基于props判断,避免在渲染外手工计算。
  • class 与 style 分工:涉及媒体查询、伪类、动画等复杂样式优先走classNames挂类;简单覆盖用styles行内样式更直接。
  • 注意替换废弃属性:若代码中仍在使用imageStyle,请迁移为styles.image,开发环境会收到废弃提示。
  • 善用全局配置:多页面共用的空状态外观,建议收敛到ConfigProviderempty组件配置中,组件局部再按需微调,避免重复样板。

小结

Empty 的classNames/styles语义化定制 API,通过rootimagedescriptionfooter四个结构节点,把原本「要么不动、要么整体重写」的空状态组件拆成了可精确打击的样式面。对象与函数两种写法分别覆盖静态与动态场景,而底层的 useMergeSemantic 统一了「组件局部 > ConfigProvider 全局」的合并优先级,并以测试用例固化了行为边界。需要实际体验完整渲染效果时,可直接参考 style-class.tsx 运行示例。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 20:20:53

Scikit-learn特征选择实战:从过滤式到嵌入式,避开数据泄漏陷阱

做机器学习项目&#xff0c;数据拿到手我第一件事不是急着调模型&#xff0c;而是先把特征列表摊开看一眼。这个习惯是踩过不少坑攒下来的——几百个特征跑完一版基线&#xff0c;效果不行&#xff0c;你根本分不清是模型的问题、样本的问题&#xff0c;还是特征里混了一堆垃圾…

作者头像 李华
网站建设 2026/9/8 20:18:00

如何快速打造轻量 Windows 11 镜像:tiny11builder 完整实战指南

如何快速打造轻量 Windows 11 镜像&#xff1a;tiny11builder 完整实战指南 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 一台用了好几年的旧笔记本&#xff0c…

作者头像 李华