# 页面与地址

用 minidocs 的 `glob` 加载的集合里，每个文件都是一个页面。这一页讲文件怎样变成页面 id 和地址，以及渲染它们的路由文件放在哪里。

## 页面 id

页面 id 是文件相对于 `glob` 的 `base` 的路径，去掉扩展名。目录里的 `index` 代表目录本身：

| 文件                   | 页面 id            |
| -------------------- | ---------------- |
| `index.mdx`          | `index`          |
| `hello.mdx`          | `hello`          |
| `guides/index.mdx`   | `guides`         |
| `guides/sidebar.mdx` | `guides/sidebar` |

文档树的 `page()`、下文的 `toHref()` 这些接收页面 id 的地方都有补全，写错的 id 是类型错误。

### 括号目录

名字带括号的目录，比如 `(basics)`，只用来整理文件，不出现在页面 id 里：

| 文件                          | 页面 id          |
| --------------------------- | -------------- |
| `(start)/index.mdx`         | `index`        |
| `(start)/install.mdx`       | `install`      |
| `guides/(basics)/links.mdx` | `guides/links` |

### 多语言站点

在 Astro 配置里设置了 `i18n` 时，集合的第一级目录是语言，比如 `zh/hello.mdx` 和 `en/hello.mdx`。页面 id 不含语言，两者都是 `hello`。

### 排除页面

草稿这类不想发布的页面，用 `glob` 的 `filter` 排除：

```ts
glob({
  base: './src/content/docs',
  pattern: '**/*.{md,mdx}',
  prefix: 'docs',
  filter: (entry) => !entry.data.draft,
});
```

`filter` 收到的是集合里的条目，已经过 schema 校验，`entry.data` 就是 frontmatter。被排除的页面不会进入集合：补全里没有它的 id，链接到它会让构建失败。想在开发服务器里预览草稿、只在构建时排除，就写成 `import.meta.env.DEV || !entry.data.draft`。

> [!WARNING]
> **在 filter 里排除，而不是 getCollection 之后**
>
> `getCollection('docs').filter(...)` 只影响你拿到的条目，类型和链接检查仍然认为这些页面存在。

## 地址

页面的地址由三部分组成：语言、集合的 `prefix`、页面 id。`prefix` 是 `docs` 时：

| 页面 id            | 地址                      | 多语言站点上的 `en`               |
| ---------------- | ----------------------- | -------------------------- |
| `index`          | `/docs/`                | `/en/docs/`                |
| `hello`          | `/docs/hello/`          | `/en/docs/hello/`          |
| `guides/sidebar` | `/docs/guides/sidebar/` | `/en/docs/guides/sidebar/` |

不设 `prefix` 时，页面放在站点根路径下。多语言站点上，语言段按 Astro 的 `i18n` 配置决定，默认语言通常不带语言段。`base` 和末尾斜杠也跟随 Astro 的配置。

在代码里拿到一个页面的链接，用 `toHref`：

```ts
toHref('docs', 'guides/sidebar'); // '/docs/guides/sidebar/'
toHref('docs', 'guides/sidebar', 'en'); // 多语言站点要传语言
```

第一个参数是集合名。在正文里链接到另一个页面，直接写文件路径，见[正文里的链接](/docs/guides/links.md)。

## 路由文件

渲染一个集合的路由文件，放在 `prefix` 对应的文件夹里，文件结构和地址结构一致：

| 站点  | 路由文件                                         |
| --- | -------------------------------------------- |
| 单语言 | `src/pages/docs/[...slug].astro`             |
| 多语言 | `src/pages/[...locale]/docs/[...slug].astro` |

`listPages` 返回的每个页面都带着它的路由参数 `page.params`，直接交给 `getStaticPaths`：

```ts
export function getStaticPaths() {
  return pages.map((page) => ({ params: page.params, props: page }));
}
```

单语言站点的参数是 `{ slug }`，多语言站点是 `{ locale, slug }`。集合首页（`index`）的 `slug` 是 `undefined`，默认语言不带前缀时 `locale` 也是 `undefined`。

路由文件不在 `prefix` 对应的文件夹里时，页面会生成到错误的地址。构建会失败，并列出没有生成的页面。
