Skip to main content
Version: 3.7.0

路由

Docusaurus的路由系统遵循单页应用程序的惯例:一个路由,一个组件。在本节中,我们将首先讨论三个内容插件(文档、博客和页面)中的路由,然后进一步讨论底层路由系统。

内容插件中的路由

每个内容插件都提供了一个routeBasePath选项。它定义了插件将其路由附加到的位置。默认情况下,文档插件将其路由放在/docs下;博客插件放在/blog下;页面插件放在/下。你可以这样理解路由结构:

任何路由都将与此嵌套路由配置匹配,直到找到合适的匹配项。例如,当给定路由/docs/configuration时,Docusaurus首先进入/docs分支,然后在docs插件创建的子路由中搜索。

更改routeBasePath可以有效地改变您网站的路由结构。例如,在仅文档模式中,我们提到为文档配置routeBasePath: '/'意味着文档插件创建的所有路由将不会有/docs前缀,但这并不妨碍您拥有由其他插件创建的更多子路由,如/blog

接下来,我们来看看这三个插件如何构建它们自己的“子路由盒子”。

页面路由

页面路由非常简单:文件路径直接映射到URL,没有其他自定义方式。有关更多信息,请参阅页面文档

用于Markdown页面的组件是@theme/MDXPage。React页面直接用作路由的组件。

博客路由

博客创建了以下路由:

  • 文章列表页面: /, /page/2, /page/3...
    • 路由可以通过 pageBasePath 选项进行自定义。
    • 组件是 @theme/BlogListPage
  • 文章页面: /2021/11/21/algolia-docsearch-migration, /2021/05/12/announcing-docusaurus-two-beta...
    • 由每个Markdown文章生成。
    • 路由可以通过slug front matter完全自定义。
    • 组件是@theme/BlogPostPage
  • 标签列表页面: /tags
    • 可以通过tagsBasePath选项自定义路由。
    • 组件是@theme/BlogTagsListPage
  • 标签页面: /tags/adoption, /tags/beta...
    • 通过每篇文章的前言部分定义的标签生成。
    • 路由始终以tagsBasePath中定义的基础路径为基础,但子路由可以通过标签的permalink字段进行自定义。
    • 组件是@theme/BlogTagsPostsPage
  • 归档页面: /archive
    • 路由可以通过 archiveBasePath 选项进行自定义。
    • 组件是 @theme/BlogArchivePage

文档路由

该文档是唯一一个创建嵌套路由的插件。在顶部,它注册了版本路径//next/2.0.0-beta.13... 这些路径提供了版本上下文,包括布局和侧边栏。这确保了在切换单个文档时,侧边栏的状态得以保留,并且您可以通过导航栏下拉菜单在不同版本之间切换,同时保持在同一个文档上。使用的组件是@theme/DocPage

在导航栏、页脚、侧边栏等由DocPage组件提供后,各个文档会在剩余空间中呈现。例如,此页面/docs/advanced/routing是从文件./versioned_docs/version-3.7.0/advanced/routing.md生成的。使用的组件是@theme/DocItem

文档的 slug 前置部分自定义了路由的最后一部分,但基础路由始终由插件的 routeBasePath 和版本的 path 定义。

文件路径和URL路径

在整个文档中,我们始终努力明确区分我们是在讨论文件路径还是URL路径。内容插件通常将文件路径直接映射到URL路径,例如,./docs/advanced/routing.md 将变为 /docs/advanced/routing。然而,使用 slug,你可以使URL完全与文件结构解耦。

在Markdown中编写链接时,您可能指的是文件路径,或者是URL路径,Docusaurus会使用几种启发式方法来确定。

  • 如果路径有 @site 前缀,它总是一个资源文件路径。
  • 如果路径有 http(s):// 前缀,它总是一个URL路径。
  • 如果路径没有扩展名,则它是一个URL路径。例如,在URL为/docs/advanced/routing的页面上的链接[page](../plugins)将链接到/docs/plugins。Docusaurus 只会在构建您的站点时检测到断开的链接(当它知道完整的路由结构时),但不会对文件的存在做出任何假设。它完全等同于在JSX文件中编写page
  • 如果路径有.md(x)扩展名,Docusaurus 会尝试将该 Markdown 文件解析为 URL,并用 URL 路径替换文件路径。
  • 如果路径有任何其他扩展名,Docusaurus 会将其视为 一个资源 并打包它。

以下目录结构可能有助于您可视化此文件→URL映射。假设在任何页面中都没有自定义slug。

A sample site structure
.
├── blog # blog plugin has routeBasePath: '/blog'
│ ├── 2019-05-28-first-blog-post.md # -> /blog/2019/05/28/first-blog-post
│ ├── 2019-05-29-long-blog-post.md # -> /blog/2019/05/29/long-blog-post
│ ├── 2021-08-01-mdx-blog-post.mdx # -> /blog/2021/08/01/mdx-blog-post
│ └── 2021-08-26-welcome
│ ├── docusaurus-plushie-banner.jpeg
│ └── index.md # -> /blog/2021/08/26/welcome
├── docs # docs plugin has routeBasePath: '/docs'; current version has base path '/'
│ ├── intro.md # -> /docs/intro
│ ├── tutorial-basics
│ │ ├── _category_.json
│ │ ├── congratulations.md # -> /docs/tutorial-basics/congratulations
│ │ └── markdown-features.mdx # -> /docs/tutorial-basics/markdown-features
│ └── tutorial-extras
│ ├── _category_.json
│ ├── manage-docs-versions.md # -> /docs/tutorial-extras/manage-docs-versions
│ └── translate-your-site.md # -> /docs/tutorial-extras/translate-your-site
├── src
│ └── pages # pages plugin has routeBasePath: '/'
│ ├── index.module.css
│ ├── index.tsx # -> /
│ └── markdown-page.md # -> /markdown-page
└── versioned_docs
└── version-1.0.0 # version has base path '/1.0.0'
├── intro.md # -> /docs/1.0.0/intro
├── tutorial-basics
│ ├── _category_.json
│ ├── congratulations.md # -> /docs/1.0.0/tutorial-basics/congratulations
│ └── markdown-features.mdx # -> /docs/1.0.0/tutorial-basics/markdown-features
└── tutorial-extras
├── _category_.json
├── manage-docs-versions.md # -> /docs/1.0.0/tutorial-extras/manage-docs-versions
└── translate-your-site.md # -> /docs/1.0.0/tutorial-extras/translate-your-site

关于内容插件的内容就这么多。让我们退一步,谈谈在Docusaurus应用程序中路由通常是如何工作的。

路由变成HTML文件

因为Docusaurus是一个服务器端渲染框架,所有生成的路由都将被服务器端渲染成静态HTML文件。如果你熟悉像Apache2这样的HTTP服务器的行为,你会理解这是如何完成的:当浏览器向路由/docs/advanced/routing发送请求时,服务器将其解释为对HTML文件/docs/advanced/routing/index.html的请求,并返回该文件。

/docs/advanced/routing 路由可以对应 /docs/advanced/routing/index.html/docs/advanced/routing.html。一些托管提供商通过尾随斜杠的存在来区分它们,并且可能容忍或不容忍另一种形式。更多信息请阅读 尾随斜杠指南

例如,上述目录的构建输出是(忽略其他资源和JS包):

Output of the above workspace
build
├── 404.html # /404/
├── blog
│ ├── archive
│ │ └── index.html # /blog/archive/
│ ├── first-blog-post
│ │ └── index.html # /blog/first-blog-post/
│ ├── index.html # /blog/
│ ├── long-blog-post
│ │ └── index.html # /blog/long-blog-post/
│ ├── mdx-blog-post
│ │ └── index.html # /blog/mdx-blog-post/
│ ├── tags
│ │ ├── docusaurus
│ │ │ └── index.html # /blog/tags/docusaurus/
│ │ ├── hola
│ │ │ └── index.html # /blog/tags/hola/
│ │ └── index.html # /blog/tags/
│ └── welcome
│ └── index.html # /blog/welcome/
├── docs
│ ├── 1.0.0
│ │ ├── intro
│ │ │ └── index.html # /docs/1.0.0/intro/
│ │ ├── tutorial-basics
│ │ │ ├── congratulations
│ │ │ │ └── index.html # /docs/1.0.0/tutorial-basics/congratulations/
│ │ │ └── markdown-features
│ │ │ └── index.html # /docs/1.0.0/tutorial-basics/markdown-features/
│ │ └── tutorial-extras
│ │ ├── manage-docs-versions
│ │ │ └── index.html # /docs/1.0.0/tutorial-extras/manage-docs-versions/
│ │ └── translate-your-site
│ │ └── index.html # /docs/1.0.0/tutorial-extras/translate-your-site/
│ ├── intro
│ │ └── index.html # /docs/1.0.0/intro/
│ ├── tutorial-basics
│ │ ├── congratulations
│ │ │ └── index.html # /docs/tutorial-basics/congratulations/
│ │ └── markdown-features
│ │ └── index.html # /docs/tutorial-basics/markdown-features/
│ └── tutorial-extras
│ ├── manage-docs-versions
│ │ └── index.html # /docs/tutorial-extras/manage-docs-versions/
│ └── translate-your-site
│ └── index.html # /docs/tutorial-extras/translate-your-site/
├── index.html # /
└── markdown-page
└── index.html # /markdown-page/

如果 trailingSlash 设置为 false,构建将生成 intro.html 而不是 intro/index.html

所有HTML文件将使用绝对URL引用其JS资源,因此为了定位正确的资源,您必须配置baseUrl字段。请注意,baseUrl不会影响生成的包的文件结构:基本URL位于Docusaurus路由系统之上的一级。您可以将urlbaseUrl的聚合视为您的Docusaurus站点的实际位置。

例如,生成的HTML将包含像这样的链接。因为绝对URL是从主机解析的,如果包放置在路径https://example.com/base/下,链接将指向https://example.com/assets/js/runtime~main.7ed5108a.js,这显然是不存在的。通过指定/base/作为基础URL,链接将正确地指向/base/assets/js/runtime~main.7ed5108a.js

本地化站点在基础URL中也包含地区代码。例如,https://docusaurus.io/zh-CN/docs/advanced/routing/ 的基础URL是 /zh-CN/

生成和访问路由

addRoute 生命周期操作用于生成路由。它将一段路由配置注册到路由树中,提供一个路由、一个组件以及组件所需的属性。属性和组件都作为路径提供给打包工具进行require,因为正如架构概述中所解释的,服务器和客户端仅通过临时文件进行通信。

所有路由都聚合在.docusaurus/routes.js中,您可以使用调试插件的路由面板查看。

在客户端,我们提供@docusaurus/router来访问页面的路由。@docusaurus/routerreact-router-dom包的重新导出。例如,你可以使用useLocation来获取当前页面的位置,并使用useHistory来访问历史对象。(它们与浏览器API不同,尽管功能相似。请参考React Router文档以获取具体的API。)

此API是SSR安全的,与仅限浏览器的window.location相反。

myComponent.js
import React from 'react';
import {useLocation} from '@docusaurus/router';

export function PageRoute() {
// React router provides the current component's route, even in SSR
const location = useLocation();
return (
<span>
We are currently on <code>{location.pathname}</code>
</span>
);
}
http://localhost:3000
We are currently on /docs/advanced/routing

从SPA重定向中逃脱

Docusaurus 构建了一个单页应用程序,其中路由转换是通过 React 路由器的 history.push() 方法完成的。此操作在客户端完成。然而,以这种方式进行路由转换的前提是我们的路由器知道目标 URL。否则,路由器会捕获此路径并显示 404 页面。

如果你将一些HTML页面放在static文件夹下,它们将被复制到构建输出中,因此可以作为你网站的一部分访问,但它不属于Docusaurus的路由系统。我们提供了一个pathname://协议,允许你以非SPA的方式重定向到你域名的另一部分,就像这个路由是一个外部链接一样。

- [pathname:///pure-html](pathname:///pure-html)
http://localhost:3000

pathname:// 协议对于引用静态文件夹中的任何内容非常有用。例如,Docusaurus 会将 所有 Markdown 静态资源转换为 require() 调用。您可以使用 pathname:// 来保持它是一个常规链接,而不是被 Webpack 哈希处理。

my-doc.md
![An image from the static](pathname:///img/docusaurus.png)

[An asset from the static](pathname:///files/asset.pdf)

Docusaurus 只会移除 pathname:// 前缀,而不会处理内容。