首页
/ ant-design Breadcrumb 面包屑:从 basic 示例到源码级渲染机制详解

ant-design Breadcrumb 面包屑:从 basic 示例到源码级渲染机制详解

2026-09-06 15:20:06作者:昌雅子Ethen

本篇以 ant-design 面包屑(Breadcrumb)组件的官方基础示例 components/breadcrumb/demo/basic.tsx 为切入点,结合 Breadcrumb.tsx 等源码实现,讲解 items 数组驱动的渲染机制、分隔符与链接的生成规则,并完整继承 中文 API 文档 中的参数说明与 browserHistory 配合方案,帮助你既能快速上手基本用法,也能理解其底层 DOM 结构与路由集成原理。

一、basic 示例:最简单的用法

官方文档对该示例的说明只有一句话(见 basic.md):"最简单的用法"。它对应的实现位于 basic.tsx,完整代码如下:

import React from 'react';
import { Breadcrumb } from 'antd';

const App: React.FC = () => {
  return (
    <Breadcrumb
      items={[
        {
          title: 'Home',
        },
        {
          title: <a href="">Application Center</a>,
        },
        {
          title: <a href="">Application List</a>,
        },
        {
          title: 'An Application',
        },
      ]}
    />
  );
};

export default App;

示例展示了 items 数组的两种典型写法,它们最终都会渲染为可点击或不可点击的面包屑项:

  1. 纯字符串 title:如 'Home''An Application'。组件不会自动为其生成链接,从源码看它最终渲染为一个 <span class="ant-breadcrumb-link">,用于表示"当前位置"这类不可跳转的项;
  2. ReactNode 形式的 title:如 <a href="">Application Center</a>。由于 title 的类型是 React.ReactNode(见 Breadcrumb.tsx 中的 BreadcrumbItemType 定义),你可以直接把任意节点——包括自带行为的 <a>、图标、路由 <Link>——塞进 title,组件只做包裹,不干预其内部行为。

items 是 5.3.0 之后推荐的传入方式。在 useItems.ts 中可以看到它的处理逻辑:只要传了 items 就直接使用;如果只传了旧版 routes,则通过 route2item 做兼容转换——把旧字段的 breadcrumbName 映射为新字段的 title,把旧版的 children(子路由)映射为 menu(下拉菜单配置),从而让老代码平滑迁移。

二、渲染机制:nav / ol / li 与分隔符生成规则

理解 basic 示例为什么能"开箱即用",需要看 Breadcrumb.tsx 的核心渲染流程。整个组件最终输出一个语义化的 <nav> 结构(Breadcrumb.tsx#L302-L313):

return (
  <BreadcrumbContext.Provider value={memoizedValue}>
    <nav ref={nativeElementRef} className={breadcrumbClassName} style={mergedStyle} {...restProps}>
      <ol>{crumbs}</ol>
    </nav>
  </BreadcrumbContext.Provider>
);
  • 根节点是 <nav>,内部是 <ol> 有序列表,每个面包屑项是 <li>,分隔符也是独立的 <li>
  • 这种结构对屏幕阅读器友好,也天然支持 RTL(从右到左)排版——当 ConfigProvider 设置 direction="rtl" 时,根节点会追加 ant-breadcrumb-rtl 类名(Breadcrumb.tsx#L276-L285)。

分隔符(separator)的合并逻辑

basic 示例没有显式传 separator,却默认显示了 /。其来源在 Breadcrumb.tsx#L140

const mergedSeparator = separator ?? contextSeparator ?? '/';

三级回退链为:组件 props 上的 separator → ConfigProvider 全局组件配置中的 separator → 硬编码默认值 /。同样的模式也用于 dropdownIcon,默认值是 <DownOutlined /> 图标。

另一个关键细节是最后一项不带分隔符。在 items 遍历渲染时(Breadcrumb.tsx#L206-L259):

const isLastItem = index === mergedItems.length - 1;
// ...
<InternalBreadcrumbItem
  // ...
  separator={isLastItem ? '' : mergedSeparator}
>

分隔符本身由 BreadcrumbSeparator.tsx 渲染,它是一个带 aria-hidden="true"<li>,且当子节点为空字符串时保持为空,不回落为 /

<li className={clsx(`${prefixCls}-separator`, mergedClassNames?.separator)} style={mergedStyles?.separator} aria-hidden="true">
  {children === '' ? children : children ?? '/'}
</li>

这保证了"末项无分隔符"的语义在 DOM 层面被精确控制。

每一项渲染成 <a> 还是 <span>

在 basic 示例中,items 既没有 href 也没有 path,所以所有项都会被渲染为 <span>。决策逻辑在 useItemRender.tsx 的 renderItem 函数

if (href !== undefined) {
  return (
    <a {...passedProps} className={clsx(`${prefixCls}-link`, className)} href={href}>
      {children}
    </a>
  );
}

return (
  <span {...passedProps} className={clsx(`${prefixCls}-link`, className)}>
    {children}
  </span>
);

href 的来源有两条路径(见 Breadcrumb.tsx#L237-L240):

  1. item 上直接写了 href,原样使用;
  2. item 使用了 path(增量拼接式路由),组件会先把每层 path 依次收集进 paths 数组,再拼成 #/${paths.join('/')} 形式作为 href,实现"每一层链接都指向到该层为止的完整路径"。

path 中的 :param 占位符则由 getPath 函数用 params 属性做替换(Breadcrumb.tsx#L97-L106):

const getPath = <T extends AnyObject = AnyObject>(params: T, path?: string) => {
  if (path === undefined) {
    return path;
  }
  let mergedPath = (path || '').replace(/^\//, '');
  Object.keys(params).forEach((key) => {
    mergedPath = mergedPath.replace(`:${key}`, params[key]!);
  });
  return mergedPath;
};

对应的带参数用法可参考官方示例 withParams.tsxtitle 中的 :id 同样会被 params 替换(见 useItemRender.tsx 的 getBreadcrumbName)。

三、API 参考(继承自官方文档)

以下参数表完整继承自 index.zh-CN.md,可结合上面的源码位置交叉验证。

Breadcrumb

参数 说明 类型 默认值 版本 全局配置
classNames 用于自定义组件内部各语义化结构的 class,支持对象或函数 Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> - 6.0.0 6.0.0
dropdownIcon 自定义下拉图标 ReactNode <DownOutlined /> 6.2.0 6.2.0
items 路由栈信息(>=5.3.0 推荐使用,旧版请使用 Breadcrumb.Item 子组件方式) ItemType[] - 5.3.0 ×
itemRender 自定义链接函数,和 react-router 配合使用 (route, params, routes, paths) => ReactNode - ×
params 路由的参数 object - ×
separator 分隔符自定义 ReactNode / 6.0.0
styles 用于自定义组件内部各语义化结构的行内 style,支持对象或函数 Record<SemanticDOM, CSSProperties> | (info: { props }) => Record<SemanticDOM, CSSProperties> - 6.0.0 6.0.0

SemanticDOM 可定位的结构点为 rootitemseparator(对应 BreadcrumbSemanticType 定义),自定义语义结构的完整示例见 style-class.tsx

RouteItemType(items 数组中每个路由项)

参数 说明 类型 默认值 版本
className 自定义类名 string -
dropdownProps 弹出下拉菜单的自定义配置 DropdownProps -
href 链接的目的地,不能和 path 共用 string -
path 拼接路径,每一层都会拼接前一个 path 信息。不能和 href 共用 string -
menu 菜单配置项 MenuProps - 4.24.0
onClick 单击事件 (e: MouseEvent) => void -
title 名称 ReactNode - 5.3.0

其中 menu 会让该项被包裹进 Dropdown:BreadcrumbItem.tsx 中的 renderBreadcrumbNode 会把 menu.items 里的 path 拼接为 ${href}${path} 的链接并整体用 <Dropdown> 包起来,触发节点带 ${prefixCls}-overlay-link 类名并附加 dropdownIcon,对应示例见 overlay.tsx

SeparatorType(独立分隔符)

const item = {
  type: 'separator', // Must have
  separator: '/',
};
参数 说明 类型 默认值 版本
type 标记为分隔符 separator 5.3.0
separator 要显示的分隔符 ReactNode / 5.3.0

在渲染循环中,type === 'separator' 的项会跳过常规 item 渲染,直接输出一个 BreadcrumbSeparator(见 Breadcrumb.tsx#L226-L228),用法示例见 separator-component.tsx

与 browserHistory 配合使用

和 react-router 一起使用时,默认生成的 url 路径是带有 # 的;如果和 browserHistory 一起使用,可以使用 itemRender 属性定义面包屑链接:

import { Link } from 'react-router';

const items = [
  {
    path: '/index',
    title: 'home',
  },
  {
    path: '/first',
    title: 'first',
    children: [
      { path: '/general', title: 'General' },
      { path: '/layout', title: 'Layout' },
      { path: '/navigation', title: 'Navigation' },
    ],
  },
  {
    path: '/second',
    title: 'second',
  },
];

function itemRender(currentRoute, params, items, paths) {
  const isLast = currentRoute?.path === items[items.length - 1]?.path;

  return isLast ? (
    <span>{currentRoute.title}</span>
  ) : (
    <Link to={`/${paths.join('/')}`}>{currentRoute.title}</Link>
  );
}

return <Breadcrumb itemRender={itemRender} items={items} />;

itemRender 的实现入口在 useItemRender.tsx:只要传入了 itemRender,就完全交由用户函数接管每一项的渲染,其接收的第四个参数 paths 正是前述 getPath 逐层累计后的路径数组,因此可以用它构造 react-router 的 to 目标;相关交互可参考 debug-routes.tsx 示例。

四、旧 API 的弃用状态与迁移提示

index.tsx 的复合组件定义可以看到,Breadcrumb.ItemBreadcrumb.Separator 均被标注为 @deprecated

type CompoundedComponent = typeof InternalBreadcrumb & {
  /** @deprecated Please use `items` instead. */
  Item: typeof BreadcrumbItem;
  /** @deprecated Please use `separator` instead. */
  Separator: typeof BreadcrumbSeparator;
};

对应的运行时警告逻辑位于 Breadcrumb.tsx#L170-L196:开发环境下,一旦检测到 routesBreadcrumb.Item / Breadcrumb.Separator 子组件用法,会通过 devUseWarning 打印弃用提示(routesitemsBreadcrumb.Item and Breadcrumb.Separatoritems),并校验 children 只允许 Breadcrumb.ItemBreadcrumb.Separator 两种类型(依据内部标记 __ANT_BREADCRUMB_ITEM / __ANT_BREADCRUMB_SEPARATOR)。因此新项目应直接使用 items 数组 + separator 属性,这也是 basic 示例所示范的标准姿势。

五、验证与延伸阅读

总结:basic 示例的"简单"体现在只传一个 items 数组;而组件内部的三级分隔符回退、path 逐层拼接与 params 替换、<a>/<span> 的按需渲染、以及 itemRender 的逃生舱口,共同构成了 ant-design 面包屑从静态层级展示到路由深度集成的一体化能力。

登录后查看全文
热门项目推荐
相关项目推荐