跳到主要内容

隐藏页面:不进导航栏的文档

本页说明本站的一个实际做法:为什么 /docs/ 那页既不在导航栏、也不在侧边栏里,却仍能正常打开。

顺便,本页自己就是这种做法的一个样本——它同样不在任何侧边栏里,只能靠直接链接进入。

一句话原理

Docusaurus 的侧边栏不是"自动列出全部文档",而是"只列出被某个 sidebar 收录的文档"。 导航栏里的 docSidebar 栏位又只是"指向某个 sidebar 的入口"。

结论

不被任何 sidebar 收录 = 不出现在侧边栏,也就不会出现在导航栏;但页面照常生成,输入 URL 就能访问。

/docs/ 就是这种情况。

它是怎么被"藏"起来的

需要三件事同时成立,少一件它就会露出来:

#条件本项目的具体位置
1文档文件不在任何 autogenerated 目录的覆盖范围docs/overview.mdx 直接放在 docs/ 根目录;而两个 sidebar 只 autogenerate in-progress/completed/
2没有任何 sidebar 显式列举sidebars.js 里只写了「施工中」「已完成」两个分类
3导航栏里没有指向它的入口docusaurus.config.jsnavbar.items 只有 🛠施工中🛠🛠已完成🛠🗨公告栏🗨

结果是:页面上任何可点的链接都到不了它,但 docs 插件依然会为每一个 .mdx 生成路由。这是"隐藏",不是"删除"。

为什么它看起来像一张独立落地页

还有两个附带的副作用,正好解释了它的观感:

  1. 这一页连左侧那一列都不渲染。 主题的文档布局里写的是 {sidebar && <DocRootLayoutSidebar />}(见 @docusaurus/theme-classic/lib/theme/DocRoot/Layout/index.js)。 当文档不属于任何 sidebar 时,它的 sidebarnull,整个侧边栏分支被跳过,正文占满宽度。 实测构建产物里,<aside> 那层侧边栏容器根本没有输出,侧边栏菜单的 ul 也不存在。
  2. URL 完全由 frontmatter 的 slug 决定。 规则是 /<routeBasePath>/<slug>;本项目 routeBasePathdocs,所以 slug: / 得到 /docs/slug: /hidden-pages 得到 /docs/hidden-pages

新增一个这样的文档

第 1 步:选位置(唯一容易做错的一步)

必须放在没有被任何 autogenerated 覆盖的目录里:

  • docs/ 根目录,或另建一个如 docs/guides/ 的目录
  • 不要放进 docs/in-progress/docs/completed/ —— 那两个目录被 autogenerate 覆盖,放进去会立刻出现在对应栏位的侧边栏里
docs/
├── in-progress/ ← 被 inProgressSidebar autogenerate,放进去就进侧边栏
│ ├── 01-changelog.mdx
│ └── 02-roadmap.mdx
├── completed/ ← 被 completedSidebar autogenerate,同理
│ ├── 01-about.mdx
│ └── 02-copyright.mdx
├── overview.mdx ← 隐藏页:不在任何 autogenerated 目录内
├── hidden-pages.mdx ← 隐藏页:本页
└── guides/ ← 新建的目录同样不会被收录
└── any-hidden.mdx

第 2 步:写 frontmatter 与正文

---
slug: /hidden-pages # 决定 URL:本页即 /docs/hidden-pages
title: 页面标题 # 浏览器标签与页面大标题
description: 一句话简介 # 写进 <meta name="description">
---

正文……

文件名只影响文档 id,URL 由 slug 说了算。不加 slug 时才会退回按文件路径生成。

第 3 步(可选):加 unlisted: true 彻底隐藏

不加也能达到"不在导航栏"的效果。加上之后,官方文档(plugin-content-docs → Markdown front matter)承诺的行为是:

Unlisted documents will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link.

落到实际产物上会多出三件事:

  • 页面 <head> 里注入 <meta name="robots" content="noindex, nofollow">
  • 不进 sitemap.xml,也不进搜索索引;
  • 正文顶部自动多出一条 caution 横幅:标题「未列出页」,文案「此页面未列出。搜索引擎不会对其索引,只有拥有直接链接的用户才能访问。」
不写 unlisted 的差别

同样不在导航里,但会被 sitemap 收录、也能被搜索引擎索引。 实测当前 /docs/ 就在 build/sitemap.xml 里,且整页没有任何 robots meta。

所以"藏"到什么程度,取决于你要不要连搜索引擎也找不到它。

第 4 步(可选):给它一个入口

"不进导航栏"不等于"不能有入口"。三种常见做法:

  1. 从别的文档里写普通链接[隐藏页面](/docs/hidden-pages)

  2. 导航栏加一个自定义项navbar.items 里的 to 型条目,完全不经过任何 sidebar):

    {to: '/docs/hidden-pages', label: '🗂内部说明🗂', position: 'left'},
  3. 只给自己留门:链接只写在某一篇文档内,导航栏一个都不放。

注意本站的断链策略

本站配置了 onBrokenLinks: 'throw'。只要写了链接,路径就必须真实存在(含 slug 是否正确),否则构建直接失败

补充:想要侧边栏,但不想被收录

如果希望这一页打开时仍显示左侧边栏(方便跳回其它文档),却不想它出现在侧边栏列表里,用 frontmatter:

displayed_sidebar: completedSidebar

displayed_sidebar 只决定"这一页显示哪个 sidebar",不会把该页加进那个 sidebar。它是"隐藏页"与"可导航"之间很好的折中。

三种"藏"的力度对比

做法侧边栏导航栏sitemap / 索引打开方式适用场景
只靠"不被 sidebar 收录"(本站 /docs/ 现状)不出现无入口仍会收录直链低调,但不介意被搜到
再加 unlisted: true不出现无入口不收录直链纯内部页、不想曝光
draft: true不出现无入口不进生产产物npm start 时可见没写完,不想上线

本站目前有哪些这类页面

页面URL入口
编译局总览/docs/首页「一起阅读吧!」按钮
本页(做法说明)/docs/hidden-pages/docs/ 总览页底部的链接
脚手架残留:introtutorial-basics/*tutorial-extras/*/docs/intro无入口

最后一行是同一个原理的另一种用法:文件保留、路由照常可访问,但 sidebars.js 刻意不列举它们,于是从侧边栏里"退出"了(Docusaurus 没有目录级的排除配置,显式枚举是唯一干净的做法)。

常见坑

  • 位置放错:只要文件落进被 autogenerated 覆盖的目录,它就自动进侧边栏,隐藏立刻失效。
  • slug 冲突:两篇文档写同一个 slug 会构建报错。
  • 文件名前缀 NN-:会被 Docusaurus 剥掉、不进 URL,只用来控制排序。别靠文件名猜 URL。
  • 想在 /docs/ 之外开页面:隐藏页做不到——slug 始终挂在 routeBasePath 之下。 要顶级路由(例如 /about)应把 mdx 放到 src/pages/(如 src/pages/about.mdx/about), 代价是没有 docs 的上下篇、侧边栏、版本这些能力。

怎么自查

改完之后跑一次构建,再直接读产物即可验收:

npm run build

然后在产物里确认三件事:

  1. 路由生成了build/docs/<你的 slug>/index.html 存在。
  2. 没渲染侧边栏:在该文件的 HTML 里搜 <aside class="theme-doc-sidebar-container,应为 0 处命中。 (要点是必须带上尖括号:正文里的 < 会被转义成 &lt;,所以"尖括号 + 标签名"这种写法永远不会被自己的正文命中。 反过来,如果只搜不含尖括号的裸类名词,这类词在正文里是原样输出的,说明文字本身就会被算进去,得到假阳性。)
  3. 没有导航入口:在首页 build/index.html 里搜你的 slug,它不应出现在导航栏链接中(导航条目的类名是 navbar__link);如果它出现在正文链接里,那只是你手动加的入口,不影响"隐藏"。