静态站点生成器中应使用内置模板变量(如Jekyll的page.breadcrumb、Hugo的.CurrentSection)结合front matter显式配置语义化层级,而非解析URL;输出HTML须符合<nav aria-label="Breadcrumb"><ol>结构,当前页用<li aria-current="page">,并可选嵌入JSON-LD增强SEO。

静态站点生成器里怎么用模板变量生成面包屑
绝大多数静态站点生成器(如 Jekyll、Hugo、Hakyll)在构建阶段就能拿到页面的完整路径、层级关系和 front matter 数据,所以面包屑不该靠 JS 在浏览器里解析 window.location.pathname,而应直接在模板中用内置变量拼出语义正确的结构。
关键不是“能不能自动”,而是“用哪组变量映射业务层级”。比如 /blog/guide/breadcrumb.md 物理路径是 3 级,但业务上可能属于「文档 > 导航组件」2 级,这就得靠 page.category 或 page.breadcrumb 这类显式字段来覆盖默认推导。
- Jekyll 中优先用
page.url+site.pages查找父级,或直接在 front matter 写breadcrumb: [{text: "指南", href: "/guide/"}, {text: "面包屑"}] - Hugo 推荐用
.CurrentSection或.Site.Menus.main关联菜单结构,比硬切.RelPermalink更可靠 - 所有生成器都支持自定义函数(Jekyll 的
{% assign %}、Hugo 的{{- $crumbs := .Page.CombinedAncestors -}}),但要注意空节点过滤——""、"index.html"、"."都得手动剔除
为什么不能直接 split("/") 生成链接
因为物理路径 ≠ 导航层级。常见反例:/en/blog/2024/05/my-post/ 拆成 ["en", "blog", "2024", "05", "my-post"] 后,会把语言前缀和归档目录全暴露为可点击项,既不符合用户心智模型,也破坏 SEO 结构化数据。
更危险的是伪目录:比如 /products/electronics/ 实际由 category.html?cat=electronics 渲染,此时按路径拼 /products/ 和 /products/electronics/ 会返回 404。
立即学习“前端免费学习笔记(深入)”;
- split("/") 前必须 trim 开头结尾的
/,否则首尾产生空字符串 - 要跳过含查询参数的路径段(
?id=123、#section) - 多语言站点需先剥离
/zh/、/en/前缀,再处理剩余部分 - 最终每级 href 必须是真实存在的绝对路径(以
/开头),不能是./或../
如何让面包屑模板兼顾语义与可访问性
模板输出的 HTML 必须严格满足无障碍要求,不是“看起来像面包屑”就行。屏幕阅读器依赖 nav + ol + aria-current="page" 这套组合识别导航意图。
错误写法:<div class="breadcrumb">Home > Blog</div> —— 读屏器只会读作“div 区域,Home 大于 Blog”,不会说明这是导航链路,也无法用键盘 Tab 跳转到各环节。
- 外层必须是
<nav aria-label="Breadcrumb">,不能是<div role="navigation"> - 内部必须用
<ol>,不用<ul>(顺序性不可省略) - 当前页用
<li aria-current="page">当前页</li>,不加href,且不能包裹<a> - 分隔符用纯文本
/插入,不要用a::before { content: "/" },否则会被读作“斜杠”
要不要加 JSON-LD Schema 增强 SEO
要,但别在模板里硬编码整个 BreadcrumbList 对象。静态生成器通常提供 page.path 或 page.url,可以结合预设的文案映射表动态组装,比手写更安全。
例如 Hugo 模板中这样写:
{{ $crumbs := .Page.Breadcrumb | default .Page.Parents.Reverse }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{{ range $i, $crumb := $crumbs }}
{
"@type": "ListItem",
"position": {{ add $i 1 }},
"name": "{{ $crumb.Title }}",
"item": "{{ $crumb.Permalink }}"
}{{ if not (last $i $crumbs) }},{{ end }}
{{ end }}
]
}
</script>注意:如果某级没有 Permalink(比如当前页),就别塞进 item 字段;position 必须从 1 开始连续编号;name 优先取 front matter 里的 breadcrumb_text,而非文件名或路径片段。
最易被忽略的是:JSON-LD 中的 item URL 必须和 HTML 中对应 <a href> 完全一致,包括 trailing slash(/products/ vs /products),否则 Google Search Console 会报结构化数据不匹配。



















