跳至内容

路由

基于文件的路由

VitePress 使用基于文件的路由,这意味着生成的 HTML 页面映射自源 Markdown 文件的目录结构。例如,给定以下目录结构

.
├─ guide
│  ├─ getting-started.md
│  └─ index.md
├─ index.md
└─ prologue.md

生成的 HTML 页面将是

index.md                  -->  /index.html (accessible as /)
prologue.md               -->  /prologue.html
guide/index.md            -->  /guide/index.html (accessible as /guide/)
guide/getting-started.md  -->  /guide/getting-started.html

生成的 HTML 可以托管在任何可以提供静态文件的 Web 服务器上。

根目录和源目录

VitePress 项目的文件结构中有两个重要的概念:**项目根目录**和**源目录**。

项目根目录

项目根目录是 VitePress 将尝试查找 .vitepress 特殊目录的位置。.vitepress 目录是 VitePress 的配置文件、开发服务器缓存、构建输出和可选主题自定义代码的保留位置。

当您从命令行运行 vitepress devvitepress build 时,VitePress 将使用当前工作目录作为项目根目录。要指定子目录作为根目录,您需要将相对路径传递给命令。例如,如果您的 VitePress 项目位于 ./docs 中,您应该运行 vitepress dev docs

.
├─ docs                    # project root
│  ├─ .vitepress           # config dir
│  ├─ getting-started.md
│  └─ index.md
└─ ...
sh
vitepress dev docs

这将导致以下源到 HTML 映射

docs/index.md            -->  /index.html (accessible as /)
docs/getting-started.md  -->  /getting-started.html

源目录

源目录是您的 Markdown 源文件所在的位置。默认情况下,它与项目根目录相同。但是,您可以通过 srcDir 配置选项进行配置。

srcDir 选项相对于项目根目录解析。例如,使用 srcDir: 'src',您的文件结构将如下所示

.                          # project root
├─ .vitepress              # config dir
└─ src                     # source dir
   ├─ getting-started.md
   └─ index.md

生成的源到 HTML 映射

src/index.md            -->  /index.html (accessible as /)
src/getting-started.md  -->  /getting-started.html

页面之间链接

在页面之间链接时,您可以使用绝对路径和相对路径。请注意,虽然 .md.html 扩展名都可以使用,但最佳实践是省略文件扩展名,以便 VitePress 可以根据您的配置生成最终 URL。

md
<!-- Do -->
[Getting Started](./getting-started)
[Getting Started](../guide/getting-started)

<!-- Don't -->
[Getting Started](./getting-started.md)
[Getting Started](./getting-started.html)

资产处理 中了解有关链接到资产(如图像)的更多信息。

链接到非 VitePress 页面

如果您想链接到网站中未由 VitePress 生成的页面,您需要使用完整 URL(在新标签页中打开)或明确指定目标

输入

md
[Link to pure.html](/pure.html){target="_self"}

输出

链接到 pure.html

注意

在 Markdown 链接中,base 会自动附加到 URL。这意味着如果您想链接到 base 之外的页面,您需要在链接中使用类似 ../../pure.html 的内容(由浏览器相对于当前页面解析)。

或者,您可以直接使用锚点标签语法

md
<a href="/pure.html" target="_self">Link to pure.html</a>

生成干净的 URL

需要服务器支持

要使用 VitePress 提供干净的 URL,需要服务器端支持。

默认情况下,VitePress 将入站链接解析为以 .html 结尾的 URL。但是,一些用户可能更喜欢没有 .html 扩展名的“干净 URL” - 例如,example.com/path 而不是 example.com/path.html

一些服务器或托管平台(例如 Netlify、Vercel、GitHub Pages)提供将类似 /foo 的 URL 映射到 /foo.html 的功能(如果存在),而无需重定向

  • Netlify 和 GitHub Pages 默认支持此功能。
  • Vercel 需要在 vercel.json 中启用 cleanUrls 选项

如果您可以使用此功能,您还可以启用 VitePress 自己的 cleanUrls 配置选项,以便

  • 页面之间的入站链接在生成时不包含 .html 扩展名。
  • 如果当前路径以 .html 结尾,路由器将执行客户端重定向到无扩展名的路径。

但是,如果您无法使用此类支持配置服务器,则必须手动采用以下目录结构

.
├─ getting-started
│  └─ index.md
├─ installation
│  └─ index.md
└─ index.md

路由重写

您可以自定义源目录结构和生成的页面之间的映射。当您有一个复杂的项目结构时,这很有用。例如,假设您有一个包含多个包的单体仓库,并且希望将文档与源文件一起放置,如下所示

.
├─ packages
│  ├─ pkg-a
│  │  └─ src
│  │      ├─ pkg-a-code.ts
│  │      └─ pkg-a-docs.md
│  └─ pkg-b
│     └─ src
│         ├─ pkg-b-code.ts
│         └─ pkg-b-docs.md

并且您希望 VitePress 页面像这样生成

packages/pkg-a/src/pkg-a-docs.md  -->  /pkg-a/index.html
packages/pkg-b/src/pkg-b-docs.md  -->  /pkg-b/index.html

您可以通过配置 rewrites 选项来实现此目的,如下所示

ts
// .vitepress/config.js
export default {
  rewrites: {
    'packages/pkg-a/src/pkg-a-docs.md': 'pkg-a/index.md',
    'packages/pkg-b/src/pkg-b-docs.md': 'pkg-b/index.md'
  }
}

rewrites 选项还支持动态路由参数。在上面的示例中,如果您有许多包,列出所有路径将很冗长。鉴于它们都具有相同的文件结构,您可以像这样简化配置

ts
export default {
  rewrites: {
    'packages/:pkg/src/(.*)': ':pkg/index.md'
  }
}

重写路径使用 path-to-regexp 包编译 - 请参阅 其文档 以了解更高级的语法。

带有重写的相对链接

启用重写后,**相对链接应基于重写后的路径**。例如,要从 packages/pkg-a/src/pkg-a-code.md 创建到 packages/pkg-b/src/pkg-b-code.md 的相对链接,您应该使用

md
[Link to PKG B](../pkg-b/pkg-b-code)

动态路由

您可以使用单个 Markdown 文件和动态数据生成许多页面。例如,您可以创建一个 packages/[pkg].md 文件,为项目中的每个包生成一个相应的页面。这里,[pkg] 段是一个路由**参数**,它将每个页面与其他页面区分开来。

路径加载器文件

由于 VitePress 是一个静态站点生成器,因此必须在构建时确定可能的页面路径。因此,动态路由页面**必须**伴随一个**路径加载器文件**。对于 packages/[pkg].md,我们需要 packages/[pkg].paths.js(也支持 .ts

.
└─ packages
   ├─ [pkg].md         # route template
   └─ [pkg].paths.js   # route paths loader

路径加载器应提供一个具有 paths 方法的对象作为其默认导出。paths 方法应返回一个包含 params 属性的对象数组。每个对象都将生成一个相应的页面。

给定以下 paths 数组

js
// packages/[pkg].paths.js
export default {
  paths() {
    return [
      { params: { pkg: 'foo' }},
      { params: { pkg: 'bar' }}
    ]
  }
}

生成的 HTML 页面将是

.
└─ packages
   ├─ foo.html
   └─ bar.html

多个参数

动态路由可以包含多个参数

文件结构

.
└─ packages
   ├─ [pkg]-[version].md
   └─ [pkg]-[version].paths.js

路径加载器

js
export default {
  paths: () => [
    { params: { pkg: 'foo', version: '1.0.0' }},
    { params: { pkg: 'foo', version: '2.0.0' }},
    { params: { pkg: 'bar', version: '1.0.0' }},
    { params: { pkg: 'bar', version: '2.0.0' }}
  ]
}

输出

.
└─ packages
   ├─ foo-1.0.0.html
   ├─ foo-2.0.0.html
   ├─ bar-1.0.0.html
   └─ bar-2.0.0.html

动态生成路径

路径加载器模块在 Node.js 中运行,并且仅在构建时执行。您可以使用任何数据动态生成路径数组,无论是本地数据还是远程数据。

从本地文件生成路径

js
import fs from 'fs'

export default {
  paths() {
    return fs
      .readdirSync('packages')
      .map((pkg) => {
        return { params: { pkg }}
      })
  }
}

从远程数据生成路径

js
export default {
  async paths() {
    const pkgs = await (await fetch('https://my-api.com/packages')).json()

    return pkgs.map((pkg) => {
      return {
        params: {
          pkg: pkg.name,
          version: pkg.version
        }
      }
    })
  }
}

在页面中访问参数

您可以使用参数将其他数据传递到每个页面。Markdown 路由文件可以通过 $params 全局属性在 Vue 表达式中访问当前页面参数

md
- package name: {{ $params.pkg }}
- version: {{ $params.version }}

您也可以通过 useData 运行时 API 访问当前页面的参数。这在 Markdown 文件和 Vue 组件中都可用。

vue
<script setup>
import { useData } from 'vitepress'

// params is a Vue ref
const { params } = useData()

console.log(params.value)
</script>

渲染原始内容

传递给页面的参数将在客户端 JavaScript 负载中序列化,因此您应该避免在参数中传递大量数据,例如从远程 CMS 获取的原始 Markdown 或 HTML 内容。

相反,您可以使用每个路径对象上的 content 属性将此类内容传递给每个页面。

js
export default {
  async paths() {
    const posts = await (await fetch('https://my-cms.com/blog-posts')).json()

    return posts.map((post) => {
      return {
        params: { id: post.id },
        content: post.content // raw Markdown or HTML
      }
    })
  }
}

然后,使用以下特殊语法将内容渲染为 Markdown 文件本身的一部分。

md
<!-- @content -->