跳到主要内容

路由

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

内容插件中的路由

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

任何路由都将与这个嵌套路由配置进行匹配,直到找到一个好的匹配项。例如,给定路由 /docs/configuration,Docusaurus 首先进入 /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 前置 matter 完全自定义。
    • 组件是 @theme/BlogPostPage
  • 标签列表页 : /tags
    • 路由可通过 tagsBasePath 选项自定义。
    • 组件是 @theme/BlogTagsListPage
  • 标签页 : /tags/adoption/tags/beta...
    • 通过每个文章前置 matter 中定义的标签生成。
    • 路由始终在 tagsBasePath 中定义基路径,但子路由可通过标签的 permalink 字段自定义。
    • 组件是 @theme/BlogTagsPostsPage
  • 归档页 : /archive
    • 路由可通过 archiveBasePath 选项自定义。
    • 组件是 @theme/BlogArchivePage

文档路由

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

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

文档的 slug 前置 matter 自定义路由的最后一部分,但基本路由始终由插件的 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 文件中编写 <a href="../plugins">page</a> 完全等效。
  • 如果路径有 .md(x) 扩展名,Docusaurus 将尝试将该 Markdown 文件解析为 URL,并将文件路径替换为 URL 路径。
  • 如果路径有任何其他扩展名,Docusaurus 将将其视为 资产 并将其捆绑。

以下目录结构可以帮助您直观地了解此文件→URL 映射。假设任何页面都没有 slug 自定义。

示例站点结构
.
├── blog # blog 插件的 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 插件的 routeBasePath: '/docs'; 当前版本的基路径为 '/'
│ ├── 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 插件的 routeBasePath: '/'
│ ├── index.module.css
│ ├── index.tsx # -> /
│ └── markdown-page.md # -> /markdown-page
└── versioned_docs
└── version-1.0.0 # 版本的基路径为 '/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 包):

上述工作区的输出
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 将包含如下链接:<link rel="preload" href="/assets/js/runtime~main.7ed5108a.js" as="script">。因为绝对 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 生命周期操作用于生成路由。它将路由配置的一部分注册到路由树中,提供路由、组件以及组件所需的 props。props 和组件都作为路径提供给打包器以进行 require,因为正如 架构概述 中所解释的,服务器和客户端仅通过临时文件进行通信。

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

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

此 API 是 服务器端渲染安全的 ,这与仅限浏览器的 window.location 不同。

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

export function PageRoute() {
// React router 提供当前组件的路由,即使在服务器端渲染中也是如此
const location = useLocation();
return (
<span>
我们当前位于 <code>{location.pathname}</code>
</span>
);
}
http://localhost:3000
我们当前位于 /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:// 协议可用于引用 static 文件夹中的任何内容。例如,Docusaurus 会将 所有 Markdown 静态资产转换为 require() 调用 。您可以使用 pathname:// 将其保留为常规链接,而不是被 Webpack 散列。

my-doc.md
![来自 static 的图片](pathname:///img/docusaurus.png)

[来自 static 的资产](pathname:///files/asset.pdf)

Docusaurus 将只去除 pathname:// 前缀而不处理内容。