antd Badge dot 模式详解:无数字小红点的显示规则、源码实现与样式定制
本篇围绕 antd Badge 组件的 dot 模式展开:通过官方示例 dot 对应的 demo 代码,结合 Badge 组件实现 与 样式层定义,讲清"无数字小红点"何时显示、何时隐藏(count 为 0 时不显示)、DOM 结构与样式如何生成,帮助你在消息提醒、通知入口等场景中正确使用并定制这一轻量级徽标形态。
1. 什么是 dot 模式
官方演示说明文档 dot.md 对这一模式的中英文描述非常凝练:
- 中文:没有具体的数字。
- 英文:This will simply display a red badge, without a specific count. If count equals 0, it won't display the dot.(仅显示一个红点,不展示具体数量;当 count 等于 0 时不显示该红点。)
这两句话点出了 dot 模式的两个核心语义:只呈现"有/无"的视觉信号,不呈现具体数值;并且在"无"的状态下(count 为 0)红点本身也不渲染。它适合那些只需要提醒"有新内容待处理"、而不需要精确计数的场景,例如通知铃铛、消息入口。
2. dot 模式的标准用法
对应 dot.md 的示例代码为 components/badge/demo/dot.tsx,完整可复制运行:
import React from 'react';
import { NotificationOutlined } from '@ant-design/icons';
import { Badge, Space } from 'antd';
const App: React.FC = () => (
<Space>
{/* 用法一:包裹图标 —— 通知铃铛右上角的红点 */}
<Badge dot>
<NotificationOutlined style={{ fontSize: 16 }} />
</Badge>
{/* 用法二:包裹链接 —— 链接右上角的红点 */}
<Badge dot>
<a href="#">Link something</a>
</Badge>
</Space>
);
export default App;
两个要点:
dot是一个布尔属性,传入即开启点状模式,此时count的数字内容不会展示;- Badge 通过
children包裹目标节点(图标、链接、头像等),红点以绝对定位锚定在子元素的右上角。
在组件文档 index.zh-CN.md 的 API 表中,dot 的完整定义为:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
dot |
不展示数字,只有一个小红点 | boolean |
false |
与之配合常用的属性还有 offset([number, number],设置状态点的位置偏移,用于微调红点落点)和 color(string,自定义小圆点的颜色,默认为主题色 badgeColor)。
3. 显示规则源码解析:dot 与 count 的判定关系
dot 模式的显示与否,在 Badge.tsx 中由一组状态量级联推导得出,理解这条判定链是掌握其行为的关键。
3.1 零值判定与 showAsDot
// components/badge/Badge.tsx(关键片段)
const numberedDisplayCount = (
(count as number) > (overflowCount as number) ? `${overflowCount}+` : count
) as string | number | null;
const isZero =
numberedDisplayCount === '0' || numberedDisplayCount === 0 || text === '0' || text === 0;
const ignoreCount = count === null || (isZero && !showZero);
随后是 dot 模式的核心开关:
const showAsDot = dot && !isZero; // L161:dot 开启且 count 非 0 才以点呈现
const mergedCount = showAsDot ? '' : numberedDisplayCount; // L163:点模式下清空数字内容
const isHidden = useMemo(() => {
const isEmpty = !isReactRenderable(mergedCount) && !isReactRenderable(text);
return (isEmpty || (isZero && !showZero)) && !showAsDot; // L165-168
}, [mergedCount, isZero, showZero, showAsDot, text]);
从这段实现可以确认三条规则:
<Badge dot />(不传 count):count默认为null(L69),isZero为 false,showAsDot为 true → 红点显示;<Badge dot count={5} />:dot优先级高于数字,mergedCount被置为空串 → 只显示红点、不显示 5;<Badge dot count={0} />:isZero为 true,showAsDot变为 false → 红点隐藏。这正是演示文档中 "If count equals 0, it won't display the dot" 的底层依据。
值得注意的一个细节:dot 状态下数字被刻意清空后,组件仍用 isHidden 综合 mergedCount、text、showAsDot 三者判断是否整体隐藏,保证 dot + count={0} 与"纯空 Badge"走的是同一条隐藏路径,不会出现空胶囊占位。
3.2 缓存机制:防止退场动画抖动
源码 L170-188 有三个缓存 ref,其中与 dot 直接相关的是:
// We will cache the dot status to avoid shaking on leaved motion
const isDotRef = useRef(showAsDot);
if (!isHidden) {
isDotRef.current = showAsDot;
}
注释明确说明其目的:在元素退场(leaved motion)期间缓存 dot 状态,避免红点在消失动画过程中形态抖动。类似地,countRef 与 displayCountRef 也在隐藏时保留上一次的值,保证退出动画期间内容不突变。
4. 渲染结构:CSSMotion + ScrollNumber 生成 ant-badge-dot
在渲染分支中(Badge.tsx),带 children 的 Badge 会渲染:
<span ref={ref} {...restProps} className={badgeClassName} style={mergedStyles.root}>
{children}
<CSSMotion
visible={!isHidden}
motionName={`${prefixCls}-zoom`}
motionAppear={false}
motionDeadline={1000}
>
{({ className: motionClassName }) => {
// ...
const isDot = isDotRef.current;
const scrollNumberCls = clsx(mergedClassNames.indicator, {
[`${prefixCls}-dot`]: isDot, // 点模式类名
[`${prefixCls}-count`]: !isDot, // 数字模式类名
[`${prefixCls}-count-sm`]: size === 'small',
[`${prefixCls}-multiple-words`]:
!isDot && displayCount && displayCount.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status,
[`${prefixCls}-color-${color}`]: isInternalColor,
});
return (
<ScrollNumber
prefixCls={scrollNumberPrefixCls}
show={!isHidden}
className={scrollNumberCls}
count={displayCount}
title={titleNode}
key="scrollNumber"
/>
);
}}
</CSSMotion>
</span>
由此可确认点模式的实际 DOM 结构(与快照测试 demo.test.tsx.snap 中 renders components/badge/demo/dot.tsx correctly 用例的输出一致):
<span class="ant-badge">
<NotificationOutlined />
<span
class="ant-scroll-number ant-badge-dot"
aria-label="Show badge dot"
style="position: absolute; ..."
></span>
</span>
几个值得了解的实现事实:
- dot 与 count 共用同一个
ScrollNumber节点,区别仅在于类名(ant-badge-dotvsant-badge-count)与是否为空内容; - 显隐动画由
CSSMotion驱动,动效名为ant-badge-zoom,motionDeadline={1000}兜底防止动画卡死; - 点模式下不会生成
ant-badge-multiple-words类(该类仅在数字超过一位时添加),因此点的大小不会随 count 增大而拉伸; title属性会落在ScrollNumber节点上,dot 场景默认无 title 内容。
5. 样式层:dot 的尺寸、颜色与投影
点状徽标的视觉规格集中在 components/badge/style/index.ts:
[`${componentCls}-dot`]: {
zIndex: token.indicatorZIndex,
width: dotSize,
minWidth: dotSize,
height: dotSize,
background: token.badgeColor,
borderRadius: '100%',
boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`,
},
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
position: 'absolute',
top: 0,
insetInlineEnd: 0,
transform: 'translate(50%, -50%)',
transformOrigin: '100% 0%',
// 若子元素含 loading 图标,红点跟随旋转动画
[`&${iconCls}-spin`]: {
animationName: antBadgeLoadingCircle,
animationDuration: '1s',
animationIterationCount: 'infinite',
animationTimingFunction: 'linear',
},
},
可以从中读出 dot 模式的设计细节:
- 尺寸由组件 Token
dotSize决定(组件 Token 定义见),宽高与最小宽统一,borderRadius: 100%保证正圆;默认宽度小于数字徽标(indicatorHeight),呼应其"轻量提示"的定位; - 锚点定位:
top: 0; insetInlineEnd: 0; transform: translate(50%, -50%),即精确落在子元素右上角交点,且insetInlineEnd为逻辑属性,天然适配 RTL 布局; - 投影
badgeShadowColor让红点在白色容器上也有清晰的描边效果;背景色来自badgeColor,可通过主题全局调整,也可通过color属性在实例级覆盖(源码 L292-295 会将非预设色写入内联background); - 子元素 loading 时的联动:当被包裹的图标处于
spin状态时,红点会继承旋转动画,视觉上与"加载中"的语义保持一致。
如需微调红点落点,使用 offset 属性即可,其解析逻辑在 Badge.tsx:offset[0] 转为数字后作用于 insetInlineEnd,offset[1] 直接作为 marginTop。
6. 测试用例对行为的固化
单元测试 components/badge/tests/index.test.tsx 用两条断言固化了 dot 的关键行为:
it('badge dot not scaling count > 9', () => {
const { container } = render(<Badge count={10} dot />);
expect(container.querySelectorAll('.ant-card-multiple-words').length).toBe(0);
});
it('badge dot not showing count == 0', () => {
const { container } = render(<Badge count={0} dot />);
expect(container.querySelectorAll('.ant-badge-dot').length).toBe(0);
});
前者验证"dot 模式不随 count 变大而变宽"(不产生多位数类名),后者验证"count 为 0 时 .ant-badge-dot 节点完全不渲染"。这两条与 dot.md 的文档描述一一对应,可作为回归验证 dot 行为的基准。
7. dot、count、status 三种形态的选择建议
| 场景 | 推荐形态 | 示例 |
|---|---|---|
| 需要精确数量、且有封顶需求 | count + overflowCount(默认 99,超出显示 99+) |
<Badge count={120} /> |
| 只需"有新消息"信号,不想暴露数量 | dot |
<Badge dot><BellOutlined /></Badge> |
| 表达系统/服务状态(成功、错误、处理中) | status + 可选 text |
<Badge status="processing" text="同步中" /> |
| 想自定义红点颜色 | dot + color(预设色键或任意 CSS 颜色值) |
<Badge dot color="gold" /> |
从源码判定链看(Badge.tsx),status/color 与 dot 属于不同分支:hasStatus 要求 ignoreCount 为真,即 count 为 null 或(为 0 且 showZero=false);当 dot 与 count 同时存在且 count 非 0 时,dot 分支先生效,数字被吞掉。混用各种形态的组合渲染由 mix 演示 及其快照覆盖。
8. 小结
dot是 Badge 的布尔开关(默认false),开启后徽标退化为纯红点,数字被清空;dot模式下count={0}会使红点整体隐藏,这是源码中showAsDot = dot && !isZero的直接结果,并有专门测试固化;- 红点与数字共用
ScrollNumber节点,靠ant-badge-dot类名区分,显隐走ant-badge-zoom动效,并有 ref 缓存防止退场抖动; - 尺寸、颜色、投影分别由组件 Token
dotSize、badgeColor、badgeShadowColor控制,实例级可用color与offset覆盖; - 完整 API 与语义化结构(
classNames/styles的indicator槽位可用于给红点追加自定义类与内联样式)见 Badge 中文文档。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00