##
剩余参数
如果路由段的数量未知,您可以使用剩余语法 - 例如,您可能可以这样实现 GitHub 的文件查看器...
```bash
/[org]/[repo]/tree/[branch]/[...file]
```
...在这种情况下,请求 `/sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md` 将使页面可用以下参数:
```js
// @noErrors
{
org: 'sveltejs',
repo: 'kit',
branch: 'main',
file: 'documentation/docs/04-advanced-routing.md'
}
```
> \[!注意\] `src/routes/a/[...rest]/z/+page.svelte` 将匹配 `/a/z`(即没有任何参数)以及 `/a/b/z` 和 `/a/b/c/z` 等等。请确保检查其余参数值的有效性,例如使用一个 [匹配器](#Matching)。
###
404 页面
余参数还允许您渲染自定义的 404 页面。给定这些路由...
```tree
src/routes/
├ marx-brothers/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
```
...文件 `marx-brothers/+error.svelte` 将不会被渲染,如果您访问 `/marx-brothers/karl`,因为没有匹配到路由。如果您想渲染嵌套的错误页面,您应该创建一个匹配任何 `/marx-brothers/*` 请求的路由,并从它返回一个 404:
```tree
src/routes/
├ marx-brothers/
+++| ├ [...path]/+++
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
```
```js
/// file: src/routes/marx-brothers/[...path]/+page.js
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function load(event) {
error(404, 'Not Found');
}
```
> \[注意\] 如果您不处理 404 情况,它们将在[`handleError`](hooks#Shared-hooks-handleError)中显示
##
可选参数
一条类似于 `[lang]/home` 的路由包含一个名为 `lang` 的参数,该参数是必需的。有时将这些参数设置为可选是有益的,因此在这个例子中,`home` 和 `en/home` 都指向同一页面。您可以通过将参数包裹在另一对括号中来实现这一点:`[[lang]]/home`
请注意,可选路由参数不能紧接在 rest 参数之后(`[...rest]/[[optional]]`),因为参数是“贪婪”匹配的,可选参数将始终未被使用。
##
匹配
一条类似于 `src/routes/fruits/[page]` 的路由会匹配 `/fruits/apple`,但也会匹配 `/fruits/rocketship`。我们不希望这样。您可以通过添加一个 *matcher* 来确保路由参数格式正确——该匹配器接受参数字符串(`"apple"` 或 `"rocketship"`)并返回 `true` 如果它是有效的——到您的 [`params`](configuration#files) 目录...
```js
/// file: src/params/fruit.js
/**
* @param {string} param
* @return {param is ('apple' | 'orange')}
* @satisfies {import('@sveltejs/kit').ParamMatcher}
*/
export function match(param) {
return param === 'apple' || param === 'orange';
}
```
...並增加您的路由:
```
src/routes/fruits/[page+++=fruit+++]
```
如果路径名不匹配,SvelteKit 将尝试匹配其他路由(使用以下指定的排序顺序),最终返回 404。
每个`params`目录下的模块都对应一个匹配器,除了可能用于单元测试匹配器的`*.test.js`和`*.spec.js`文件。
> \[!注意\] 匹配器在服务器和浏览器上同时运行。
##
排序
可能多个路由会匹配给定的路径。例如,以下每个路由都会匹配 `/foo-abc`:
```bash
src/routes/[...catchall]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/foo-abc/+page.svelte
```
SvelteKit 需要知道正在请求哪个路由。为了做到这一点,它根据以下规则对它们进行排序...
* 更具体的路由优先级更高(例如,没有参数的路由比有一个动态参数的路由更具体,依此类推)
* 参数带有[匹配器](#Matching)(`[name=type]`)的优先级高于不带(`[name]`)的参数
* `[[可选]]` 和 `[...其余]` 参数除非是路由的最后一部分,否则将被忽略,在这种情况下,它们将被赋予最低优先级。换句话说,`x/[[y]]/z` 在排序方面等同于 `x/z`。
* 字母顺序解决冲突
...导致这种排序,意味着`/foo-abc`将调用 `src/routes/foo-abc/+page.svelte` ,而`/foo-def`将调用 `src/routes/foo-[c]/+page.svelte` ,而不是更不具体的路由:
```bash
src/routes/foo-abc/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/[...catchall]/+page.svelte
```
##
编码
某些字符在文件系统中不能使用 —— Linux 和 Mac 上的 `/`,Windows 上的 `\ / : * ? " < > |`。字符 `#` 和 `%` 在 URL 中有特殊含义,字符 `[ ] ( )` 对 SvelteKit 有特殊含义,因此这些字符也不能直接用作路由的一部分。
要在这条路由中使用这些字符,您可以使用十六进制转义序列,其格式为`[x+nn]`,其中`nn`是十六进制字符代码:
* `—` `[[x+5c]]`
* `/` — `[x+2f]`
* `:` — `[x+3a]`
* `*` — `[x+2a]`
* `?` — `[x+3f]`
* `"` — `[x+22]`
* `<` — `[x+3c]<`
* `>` — `[x+3e]`
* `|` — `[x+7c]`
* `井号` — `[x+23]`
* `%` — `[x+25]`
* `]` — `][x+5b]`
* `]` — `[x+5d]`
* `(` — `[x+28]`
* `)` — `[x+29]`
例如,要创建一个 `/smileys/:-)` 路由,你需要创建一个 `src/routes/smileys/[x+3a]-[x+29]/+page.svelte` 文件。
您可以使用 JavaScript 确定字符的十六进制代码:
```js
':'.charCodeAt(0).toString(16); // '3a', hence '[x+3a]'
```
您也可以使用 Unicode 转义序列。通常情况下,您不需要这样做,因为您可以直接使用未编码的字符,但如果——出于某种原因——您不能使用包含表情符号的文件名,例如,那么您可以使用转义字符。换句话说,这些是等效的:
```
src/routes/[u+d83e][u+dd2a]/+page.svelte
src/routes/🤪/+page.svelte
```
Unicode 转义序列的格式为`[u+nnnn]`,其中`nnnn`是介于`0000`和`10ffff`之间的有效值。(与 JavaScript 字符串转义不同,表示大于`ffff`的码点时无需使用代理对。)要了解更多关于 Unicode 编码的信息,请参阅[《Unicode 编程》](https://unicodebook.readthedocs.io/unicode_encodings.html)。
> \[!注意\] 由于 TypeScript [难以处理](https://github.com/microsoft/TypeScript/issues/13399) 以点号开头的目录,您在创建例如 [`.well-known`](https://en.wikipedia.org/wiki/Well-known_URI) 路由时可能发现对这些字符进行编码很有用: `src/routes/[x+2e]well-known/...`
##
高级布局
默认情况下,*布局层次结构*与*路由层次结构*相匹配。在某些情况下,这可能不是您想要的结果。
###
(組)
可能您有一些路由是 'app' 路由,应该使用一个布局(例如 `/dashboard` 或 `/item`),而其他的是 'marketing' 路由,应该使用不同的布局(`/about` 或 `/testimonials`)。我们可以将这些路由分组到一个以括号包裹的目录中——与普通目录不同,`(app)` 和 `(marketing)` 不会影响它们内部路由的 URL 路径名:
```tree
src/routes/
+++│ (app)/+++
│ ├ dashboard/
│ ├ item/
│ └ +layout.svelte
+++│ (marketing)/+++
│ ├ about/
│ ├ testimonials/
│ └ +layout.svelte
├ admin/
└ +layout.svelte
```
您也可以直接在`(group)`中放置一个`+page`,例如,如果`/`应该是一个`(app)`或`(marketing)`页面。
###
跳出布局
根布局适用于您应用的每个页面 — 如果省略,则默认为 `render children()`。如果您想某些页面具有与其他页面不同的布局层次结构,则可以将整个应用放入一个或多个组 *除外* 应该继承公共布局的路由。
在上述示例中,`/admin` 路由既不继承 `(app)` 也不继承 `(marketing)` 布局。
###
+页面@
页面可以在路由级别逐个突破当前布局层次结构。假设我们有一个位于上一个示例中的 `(app)` 组内的 `/item/[id]/embed` 路由:
```tree
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
+++│ │ │ │ └ +page.svelte+++
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte
```
通常,这将继承根布局、`(app)`布局、`item`布局和`[id]`布局。我们可以通过追加`@`后跟段名称来重置为这些布局之一——或者对于根布局,为空字符串。在这个例子中,我们可以从以下选项中选择:
* `+page@[id].svelte` - 继承自 `src/routes/(app)/item/[id]/+layout.svelte`
* `+page@item.svelte` - 继承自 `src/routes/(app)/item/+layout.svelte`
* `+page@(app).svelte` - 继承自 `src/routes/(app)/+layout.svelte`
* `+page@.svelte` - 继承自 `src/routes/+layout.svelte`
```tree
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
+++│ │ │ │ └ +page@(app).svelte+++
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte
```
###
+布局@
与页面类似,布局也可以*自身*跳出其父布局层次结构,使用相同的技巧。例如,一个`+layout@.svelte`组件将重置其所有子路由的层次结构。
```
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte // uses (app)/item/[id]/+layout.svelte
│ │ │ ├ +layout.svelte // inherits from (app)/item/+layout@.svelte
│ │ │ └ +page.svelte // uses (app)/item/+layout@.svelte
│ │ └ +layout@.svelte // inherits from root layout, skipping (app)/+layout.svelte
│ └ +layout.svelte
└ +layout.svelte
```
###
何时使用布局组
不是所有用例都适合布局分组,也不必强迫自己使用它们。可能你的用例会导致复杂的`(分组)`嵌套,或者你不想为单个异常引入一个`(分组)`。使用其他方法,如组合(可重用的`load`函数或 Svelte 组件)或 if 语句来实现你的目标是完全可行的。以下示例显示了一个布局,它回滚到根布局并重用其他布局也可以使用的组件和函数:
```svelte
{@render children()}
```
```js
/// file: src/routes/nested/route/+layout.js
// @filename: ambient.d.ts
declare module "$lib/reusable-load-function" {
export function reusableLoad(event: import('@sveltejs/kit').LoadEvent): Promise>;
}
// @filename: index.js
// ---cut---
import { reusableLoad } from '$lib/reusable-load-function';
/** @type {import('./$types').PageLoad} */
export function load(event) {
// Add additional logic here, if needed
return reusableLoad(event);
}
```
##
进一步阅读
* [
教程:高级路由](/tutorial/kit/optional-params)