路由
Docusaurus 的路由系统遵循单页应用惯例:一条路由,一个组件。 在本节中,我们将首先讨论三个内容插件(文 档、博客和页面)中的路由,然后进一步讨论底层路由系统。
内容插件中的路由
每个内容插件都提供一个routeBasePath
选项。它定义了插件将路由附加到哪里。默认情况下,docs 插件把它的路由放在/docs
下;博客插件/blog
;和页面插件/
。你可以这样考虑路由结构:
任何路由都将与这个嵌套的路由配置匹配,直到找到一个好的匹配。例如,当给定一个路由/docs/configuration
时,Docusaurus 首先进入/docs
分支,然后在 docs 插件创建的子路由中搜索。
更改routeBasePath
可以有效地改变站点的路由结构。例如,在docs-only 模式中,我们提到为 docs 配置routeBasePath: '/'
意味着 docs 插件创建的所有路由都不会有/docs
前缀,但它并不能阻止其他插件创建更多像/blog
这样的子路由。
接下来,让我们看看这三个插件是如何构建它们自己的子路由盒子
的。
页面路由
页面路由很简单:文件路径直接映射到 URLs,不需要任何其他自定义方式。请参阅pages 文档了解更多信息。
用于 Markdown 页面的组件是@theme/MDXPage
。React 页面被直接用作路由的组件。
博客路由
该博客创建了以下路由:
- Posts list pages:
/
,/page/2
,/page/3
...- The component is
@theme/BlogListPage
.
- The component is
- Post pages:
/2021/11/21/algolia-docsearch-migration
,/2021/05/12/announcing-docusaurus-two-beta
...- Generated from each Markdown post.
- The routes are fully customizable through the
slug
front matter. - The component is
@theme/BlogPostPage
.
- Tags list page:
/tags
- The route is customizable through the
tagsBasePath
option. - The component is
@theme/BlogTagsListPage
.
- The route is customizable through the
- Tag pages:
/tags/adoption
,/tags/beta
...- Generated through the tags defined in each post's front matter.
- The routes always have base defined in
tagsBasePath
, but the subroutes are customizable through the tag'spermalink
field. - The component is
@theme/BlogTagsPostsPage
.
- Archive page:
/archive
- The route is customizable through the
archiveBasePath
option. - The component is
@theme/BlogArchivePage
.
- The route is customizable through the
文档路由
The docs is the only plugin that creates nested routes. At the top, it registers version paths: /
, /next
, /2.0.0-beta.13
... which provide the version context, including the layout and sidebar. This ensures that when switching between individual docs, the sidebar's state is preserved, and that you can switch between versions through the navbar dropdown while staying on the same doc. The component used is @theme/DocPage
.
The individual docs are rendered in the remaining space after the navbar, footer, sidebar, etc. have all been provided by the DocPage
component. For example, this page, /docusaurus-docs/docs/next/advanced/routing
, is generated from the file at ./docs/advanced/routing.md
. The component used is @theme/DocItem
.
The doc's slug
front matter customizes the last part of the route, but the base route is always defined by the plugin's routeBasePath
and the version's path
.
文件路径和 URL 路径
Throughout the documentation, we always try to be unambiguous about whether we are talking about file paths or URL paths. Content plugins usually map file paths directly to URL paths, for example, ./docs/advanced/routing.md
will become /docs/advanced/routing
. However, with slug
, you can make URLs totally decoupled from the file structure.
When writing links in Markdown, you could either mean a file path, or a URL path, which Docusaurus would use several heuristics to determine.
- If the path has a
@site
prefix, it is always an asset file path. - If the path has an
http(s)://
prefix, it is always a URL path. - If the path doesn't have an extension, it is a URL path. For example, a link
[page](../plugins)
on a page with URL/docs/advanced/routing
will link to/docs/plugins
. Docusaurus will only detect broken links when building your site (when it knows the full route structure), but will make no assumptions about the existence of a file. It is exactly equivalent to writing<a href="../plugins">page</a>
in a JSX file. - If the path has an
.md(x)
extension, Docusaurus would try to resolve that Markdown file to a URL, and replace the file path with a URL path. - If the path has any other extension, Docusaurus would treat it as an asset and bundle it.
The following directory structure may help you visualize this file → URL mapping. Assume that there's no slug customization in any page.
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/congratulations
│ └── 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
So much about content plugins. Let's take one step back and talk about how routing works in a Docusaurus app in general.
路由变成 HTML 文件
Because Docusaurus is a server-side rendering framework, all routes generated will be server-side rendered into static HTML files. If you are familiar with the behavior of HTTP servers like Apache2, you will understand how this is done: when the browser sends a request to the route /docs/advanced/routing
, the server interprets that as request for the HTML file /docs/advanced/routing/index.html
, and returns that.
The /docs/advanced/routing
route can correspond to either /docs/advanced/routing/index.html
or /docs/advanced/routing.html
. Some hosting providers differentiate between them using the presence of a trailing slash, and may or may not tolerate the other. Read more in the trailing slash guide.
For example, the build output of the directory above is (ignoring other assets and JS bundle):
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/
If trailingSlash
is set to false
, the build would emit intro.html
instead of intro/index.html
.
All HTML files will reference its JS assets using absolute URLs, so in order for the correct assets to be located, you have to configure the baseUrl
field. Note that baseUrl
doesn't affect the emitted bundle's file structure: the base URL is one level above the Docusaurus routing system. You can see the aggregate of url
and baseUrl
as the actual location of your Docusaurus site.
For example, the emitted HTML would contain links like <link rel="preload" href="/assets/js/runtime~main.7ed5108a.js" as="script">
. Because absolute URLs are resolved from the host, if the bundle placed under the path https://example.com/base/
, the link will point to https://example.com/assets/js/runtime~main.7ed5108a.js
, which is, well, non-existent. By specifying /base/
as base URL, the link will correctly point to /base/assets/js/runtime~main.7ed5108a.js
.
Localized sites have the locale as part of the base URL as well. For example, https://docusaurus.io/zh-CN/docs/advanced/routing/
has base URL /zh-CN/
.
生成和访问路由
The addRoute
lifecycle action is used to generate routes. It registers a piece of route config to the route tree, giving a route, a component, and props that the component needs. The props and the component are both provided as paths for the bundler to require
, because as explained in the architecture overview, server and client only communicate through temp files.
All routes are aggregated in .docusaurus/routes.js
, which you can view with the debug plugin's routes panel.
On the client side, we offer @docusaurus/router
to access the page's route. @docusaurus/router
is a re-export of the react-router-dom
package. For example, you can use useLocation
to get the current page's location, and useHistory
to access the history object. (They are not the same as the browser API, although similar in functionality. Refer to the React Router documentation for specific APIs.)
This API is SSR safe, as opposed to the browser-only window.location
.
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>
);
}
/docusaurus-docs/docs/next/advanced/routing
逃避 SPA 重定向
Docusaurus builds a single-page application, where route transitions are done through the history.push()
method of React router. This operation is done on the client side. However, the prerequisite for a route transition to happen this way is that the target URL is known to our router. Otherwise, the router catches this path and displays a 404 page instead.
If you put some HTML pages under the static
folder, they will be copied to the build output and therefore become accessible as part of your website, yet it's not part of the Docusaurus route system. We provide a pathname://
protocol that allows you to redirect to another part of your domain in a non-SPA fashion, as if this route is an external link. Try the following two links:
- [/pure-html](/pure-html)
- [pathname:///pure-html](pathname:///pure-html)
The first link will not trigger a "broken links detected" check during the production build, because the respective file actually exists. Nevertheless, when you click on the link, a "page not found" will be displayed until you refresh.
The pathname://
protocol is useful for referencing any content in the static folder. For example, Docusaurus would convert all Markdown static assets to require() calls. You can use pathname://
to keep it a regular link instead of being hashed by Webpack.
![An image from the static](pathname:///img/docusaurus.png)
[An asset from the static](pathname:///files/asset.pdf)
Docusaurus will only strip the pathname://
prefix without processing the content.