合规面包屑必须用<nav aria-label="breadcrumb">包裹且使用<ol>,各级为可跳转<a>,末项加aria-current="page",分隔符用CSS实现。

面包屑必须用 <nav> 包裹,且带 aria-label="breadcrumb"
纯视觉的“首页 > 产品 > 详情”写法没有语义,对屏幕阅读器不可见,也不被搜索引擎识别为导航路径。真正合规的面包屑需同时满足两个硬性条件:外层是 <nav> 元素,且必须声明 aria-label="breadcrumb"(不能只写 role="navigation",也不能用 class 或 id 替代)。否则辅助技术会把它当成普通文本或忽略。
-
<ol>比<ul>更合适:层级有明确先后顺序,<ol>天然表达“路径深度”,部分读屏器会播报序号(如“第1项,首页”),增强上下文感知 - 每级必须是真实可跳转的
<a>,末项用<a aria-current="page">标记,不能是纯文本或<span> - 避免用
<div>+ CSS Flex 模拟列表结构——它绕过了语义树,爬虫和读屏器都看不到路径关系
为什么不用 <section> 或 <aside> 套面包屑
<section> 表示内容逻辑分块,比如“评论区”“相关推荐”,不是导航;<aside> 是附属信息容器,典型场景是术语解释、作者简介——删掉它主内容依然完整。而面包屑本质是用户可操作的导航入口集合,属于页面级导航体系的一部分,和主导航、页脚导航同属一类,所以必须归入 <nav>。
- 若页面含多个
<nav>(如顶部主导航 + 面包屑 + 页脚链接),必须用aria-label明确区分,例如:<nav aria-label="主导航">、<nav aria-label="breadcrumb">、<nav aria-label="页脚导航"> - 不要用
aria-labelledby或id引用外部标题——面包屑本身没有可见标题,aria-label是唯一可靠方式
<ol> 内部结构要点:链接、当前页、分隔符
<ol> 的每个 <li> 应只包含一个 <a>(或末项为 <a aria-current="page">),不要嵌套 <span> 或额外标签干扰语义流。
- 当前页链接仍要保留
href属性(可指向自身 URL),仅靠aria-current="page"不足以维持语义完整性 - 分隔符(如
/或>)必须用 CSS::after实现,不能写死在 HTML 里,否则会被读屏器朗读为“斜杠”“大于号”,干扰路径理解 - 示例片段:
<nav aria-label="breadcrumb"> <ol> <li><a href="/">首页</a></li> <li><a href="/products">产品</a></li> <li><a href="/products/editor" aria-current="page">编辑器</a></li> </ol> </nav>
前端框架里别从 useRoute().path 动态解析面包屑
直接对 URL 字符串做正则拆分(如 path.split('/').filter(Boolean))在真实项目中极易出错:
立即学习“前端免费学习笔记(深入)”;
- 路由参数如
/post/:id会被误渲染成“post :id”,而非“文章详情” - 同一路径不同语义(如
/settings在个人页 vs 团队页)无法区分 - 国际化时路径不变但文字要变,硬解析无法适配
- 带查询参数的路径(如
/docs?version=latest)拆分会生成无效链接
正确做法是在路由定义中显式声明 meta: { breadcrumb: '用户设置' },组件内遍历 useRoute().matched 提取所有匹配项的 meta.breadcrumb 拼接。这确保了面包屑基于业务语义,而非运行时路径字符串。
容易被忽略的一点:面包屑层级必须与用户认知路径一致,而不是服务器目录结构。比如后台 API 文档页显示 /v2/api/docs 这种路径,应该用 <code> 块单独展示,另建一套基于功能的面包屑(如“开发者中心 > API 文档 > 编辑器接口”)。



















