Skip to main content

ルーティング

Docusaurus のルーティングシステムは、1つのルートに1つのコンポーネントというシングルページアプリケーションの規約に従っています。 このセクションでは、ドキュメント、ブログ、ページといった3つのコンテンツプラグイン内のルーティングについて説明し、内部のルーティングシステムについて話を進めます。

コンテンツプラグインのルーティング

すべてのコンテンツプラグインは routeBasePath オプションを提供しています。 プラグインがどこのルートに追加されるかを定義するものです。 デフォルトでは、ドキュメントプラグインはルートを /docs に、ブログのプラグインは /blog に、ページプラグインは / になっています。 ルート構造は次のように考えることができます。

ルートはこのネストされたルート設定に照らして順に照合され、適切なマッチが見つかるまで検索が続きます。 例えば、ルートが /docs/configuration の場合、Docusaurusはまず /docs ブランチに入り、続いてドキュメントプラグインが作成したサブルートの中から該当するものを探します。

routeBasePath を変更すると、サイトのルート構造が実質的に変わります。 例えば、Docs-only mode の説明で触れたように、docsプラグインの routeBasePath: '/'`\` を設定すると、docsプラグインが生成するすべてのルートから /docs\ プレフィックスがなくなります。ただし、他のプラグインが作成する ``/blog\ のようなサブルートが存在することは妨げません。

次に、3つのプラグインがそれぞれどのように「サブルートの枠組み」を構築しているかを見てみましょう。

ページのルーティング

ページのルーティングはシンプルで、ファイルのパスがそのままURLに対応し、カスタマイズ方法はほとんどありません。 詳しくは、pagesドキュメントのルーティング解説をご覧ください。

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 フロントマターで完全にカスタマイズできます。
    • コンポーネントは @theme/BlogPostPage です。
  • タグ一覧のページ: /tags
    • ルートは tagsBasePath オプションでカスタマイズできます。
    • コンポーネントは @theme/BlogTagsListPage です。
  • タグのページ: /tags/adoption, /tags/beta...
    • 各投稿のフロントマターで定義されたタグから生成されます。
    • ルートは常に tagsBasePathで定義されていますが、タグの parmalink フィールドでサブルートをカスタマイズできます。
    • コンポーネントは @theme/BlogTagsPostsPage です。
  • アーカイブページ: /archive
    • ルートは archiveBasePath オプションでカスタマイズできます。
    • コンポーネントは @theme/BlogArchivePage です。

ドキュメントのルーティング

Docsはネストされたルートを作成する唯一のプラグインです。 最上位では、バージョンパス//next/2.0.0-beta.13 など)を登録し、レイアウトやサイドバーなどのバージョンコンテキストを提供しています。 これにより、個別のドキュメント間を移動してもサイドバーの状態が保持され、ナビゲーションバーのドロップダウンからバージョンを切り替えても、同じドキュメント上に留まることができます。 使用されるコンポーネントは ``@theme/DocPage\ です。

個々のドキュメントは、ナビバーやフッター、サイドバーなどが DocPage`\` コンポーネントによって配置された残りのスペースにレンダリングされます。 例えば、このページ `<URLPath />` は、ファイル `<FilePath />` から生成されています。 使用されているコンポーネントは @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 ファイルで <a href="../plugins">page</a> を書くのと全く同じです。
  • パスに .md(x) 拡張子がある場合、Docusaurus は Markdown ファイルのパスを 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'; current バージョンは 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プラグインの routeBasePath は '/'
│ ├── index.module.css
│ ├── index.tsx # -> /
│ └── markdown-page.md # -> /markdown-page
└── versioned_docs
└── version-1.0.0 # バージョンの 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はサーバーサイドレンダリング(SSR)フレームワークのため、生成されるすべてのルートはサーバーサイドでレンダリングされ、静的なHTMLファイルとして出力されます。 もしApache2の動作に馴染みがあれば、この仕組みは理解しやすいでしょう。ブラウザが /docs/advanced/routing`\` へのリクエストを送ると、サーバーはそれを /docs/advanced/routing/index.html\ というHTMLファイルのリクエストとして解釈し、そのファイルを返します。

/docs/advanced/routing`\` のルートは、/docs/advanced/routing/index.html\ または ``/docs/advanced/routing.html\ のどちらかに対応する場合があります。 ホスティングプロバイダーによっては、末尾のスラッシュの有無で両者を区別し、どちらか一方のみを許容する場合があります。 詳しくは、trailing slash guideをご覧ください。

例えば、上記ディレクトリ構成のビルド出力は以下のようになります(その他のアセットや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/index.html`\` の代わりに intro.html\ を出力します。

すべてのHTMLファイルはJSアセットを絶対URLで参照するため、正しいアセットを読み込むには baseUrl`\` の設定が必要です。 なお、baseUrl\ は出力されるバンドルのファイル構造には影響しません。base URLはDocusaurusのルーティングシステムの一段上の階層を指します。 url`\` と baseUrl\ の組み合わせが、実際のDocusaurusサイトの公開場所(Url)として機能します。

例えば、出力される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/\ を base URL として指定すれば、リンクは正しく ``/base/assets/js/runtime~main.7ed5108a.js\ を指すようになります。

ローカライズされたサイトでは、ロケールが base URL の一部として含まれます。 例えば、https://docusaurus.io/zh-CN/docs/advanced/routing/`\` の場合、base URL は /zh-CN/\ となります。

ルートの生成とアクセス

addRoute`\` ライフサイクルアクションは、ルートを生成するために使われます。 ルートツリーにルート設定の一部を登録し、ルートやコンポーネント、そしてそのコンポーネントが必要とするプロパティを指定します。 コンポーネントとプロパティはどちらもバンドラーが require\ できるパスとして提供されます。これは、アーキテクチャ概要で説明されているように、サーバーとクライアントが一時ファイルを介してのみ通信するためです。

すべてのルートは ``.docusaurus/routes.js\ にまとめられており、デバッグプラグインのroutesパネルから確認できます。

クライアント側では、ページのルートにアクセスするための @docusaurus/router`\` が提供されています。 @docusaurus/router\ は、react-router-dom パッケージの再エクスポートです。 例えば、useLocation`\` を使うと現在のページの[location](https://developer.mozilla.org/en-US/docs/Web/API/Location)を取得でき、useHistory\historyオブジェクトにアクセスできます。 (機能的には似ていますが、ブラウザのAPIとは厳密には異なります。 詳しいAPIについては、React Routerのドキュメントを参照してください)

このAPIは、ブラウザ専用の ``window.location\ とは異なり、サーバーサイドレンダリング(SSR)対応となっています。

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

export function PageRoute() {
// React RouterはSSR環境でも現在のコンポーネントのルート情報を提供します
const location = useLocation();
return (
<span>
現在のページは <code>{location.pathname}</code> です
</span>
);
}
http://localhost:3000
現在のページは /ja/docs/advanced/routing です

SPA のリダイレクトを回避する

Docusaurusは、ルートの遷移をReact Routerの ``history.push()\ メソッドで行うシングルページアプリケーション(SPA)として構築されています。 この操作はクライアント側で行われます。 ただし、この方法でルート遷移が行われるためには、遷移先のURLがDocusaurusのルーターに認識されている必要があります。 そうでない場合、ルーターはそのパスを検知して404ページを表示します。

static`\` フォルダに配置したHTMLファイルはビルド時に出力先にコピーされ、そのままウェブサイト上でアクセス可能になります。ただし、これらはDocusaurusのルーティングシステムの管理外です。 pathname://\ プロトコルを用意しており、これを使うとドメイン内の別の場所へ、SPAを介さずに外部リンクのようにリダイレクトできます。

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

pathname://`\` プロトコルは、`static` フォルダ内のコンテンツを参照するときに便利です。 例えば、Docusaurusは[すべてのMarkdown静的アセットをrequire()呼び出しに変換します](../guides/markdown-features/markdown-features-assets.mdx#static-assets)。 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://\ プレフィックスを取り除くだけで、その後のコンテンツ自体は処理しません。