首页
/ antd Badge dot 模式详解:无数字小红点的显示规则、源码实现与样式定制

antd Badge dot 模式详解:无数字小红点的显示规则、源码实现与样式定制

2026-09-06 13:29:44作者:毕习沙Eudora

本篇围绕 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;

两个要点:

  1. dot 是一个布尔属性,传入即开启点状模式,此时 count 的数字内容不会展示;
  2. Badge 通过 children 包裹目标节点(图标、链接、头像等),红点以绝对定位锚定在子元素的右上角。

在组件文档 index.zh-CN.md 的 API 表中,dot 的完整定义为:

参数 说明 类型 默认值
dot 不展示数字,只有一个小红点 boolean false

与之配合常用的属性还有 offset[number, number],设置状态点的位置偏移,用于微调红点落点)和 colorstring,自定义小圆点的颜色,默认为主题色 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]);

从这段实现可以确认三条规则:

  1. <Badge dot />(不传 count)count 默认为 null(L69),isZero 为 false,showAsDot 为 true → 红点显示;
  2. <Badge dot count={5} />dot 优先级高于数字,mergedCount 被置为空串 → 只显示红点、不显示 5;
  3. <Badge dot count={0} />isZero 为 true,showAsDot 变为 false → 红点隐藏。这正是演示文档中 "If count equals 0, it won't display the dot" 的底层依据。

值得注意的一个细节:dot 状态下数字被刻意清空后,组件仍用 isHidden 综合 mergedCounttextshowAsDot 三者判断是否整体隐藏,保证 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 状态,避免红点在消失动画过程中形态抖动。类似地,countRefdisplayCountRef 也在隐藏时保留上一次的值,保证退出动画期间内容不突变。

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.snaprenders 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-dot vs ant-badge-count)与是否为空内容;
  • 显隐动画由 CSSMotion 驱动,动效名为 ant-badge-zoommotionDeadline={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 模式的设计细节:

  1. 尺寸由组件 Token dotSize 决定组件 Token 定义见),宽高与最小宽统一,borderRadius: 100% 保证正圆;默认宽度小于数字徽标(indicatorHeight),呼应其"轻量提示"的定位;
  2. 锚点定位top: 0; insetInlineEnd: 0; transform: translate(50%, -50%),即精确落在子元素右上角交点,且 insetInlineEnd 为逻辑属性,天然适配 RTL 布局;
  3. 投影 badgeShadowColor 让红点在白色容器上也有清晰的描边效果;背景色来自 badgeColor,可通过主题全局调整,也可通过 color 属性在实例级覆盖(源码 L292-295 会将非预设色写入内联 background);
  4. 子元素 loading 时的联动:当被包裹的图标处于 spin 状态时,红点会继承旋转动画,视觉上与"加载中"的语义保持一致。

如需微调红点落点,使用 offset 属性即可,其解析逻辑在 Badge.tsxoffset[0] 转为数字后作用于 insetInlineEndoffset[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/colordot 属于不同分支:hasStatus 要求 ignoreCount 为真,即 count 为 null 或(为 0 且 showZero=false);当 dotcount 同时存在且 count 非 0 时,dot 分支先生效,数字被吞掉。混用各种形态的组合渲染由 mix 演示 及其快照覆盖。

8. 小结

  • dot 是 Badge 的布尔开关(默认 false),开启后徽标退化为纯红点,数字被清空;
  • dot 模式下 count={0} 会使红点整体隐藏,这是源码中 showAsDot = dot && !isZero 的直接结果,并有专门测试固化;
  • 红点与数字共用 ScrollNumber 节点,靠 ant-badge-dot 类名区分,显隐走 ant-badge-zoom 动效,并有 ref 缓存防止退场抖动;
  • 尺寸、颜色、投影分别由组件 Token dotSizebadgeColorbadgeShadowColor 控制,实例级可用 coloroffset 覆盖;
  • 完整 API 与语义化结构(classNames/stylesindicator 槽位可用于给红点追加自定义类与内联样式)见 Badge 中文文档
登录后查看全文
热门项目推荐
相关项目推荐