跳至主要内容

高级

高级路由

在 GitHub 上编辑此页面

剩余参数

如果路由段的数量未知,则可以使用剩余语法 — 例如,您可以像这样实现 GitHub 的文件查看器...

/[org]/[repo]/tree/[branch]/[...file]

...在这种情况下,对 /sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md 的请求将导致以下参数可用于页面

ts
{
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 等。请务必检查剩余参数的值是否有效,例如使用 匹配器

404 页面

剩余参数还允许您呈现自定义 404。给定这些路由...

src/routes/
├ marx-brothers/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte

...如果您访问 /marx-brothers/karl,则不会呈现 marx-brothers/+error.svelte 文件,因为没有匹配的路由。如果您想呈现嵌套错误页面,则应创建一个匹配任何 /marx-brothers/* 请求的路由,并从中返回 404

src/routes/
├ marx-brothers/
| ├ [...path]/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
src/routes/marx-brothers/[...path]/+page.js
ts
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function load(event) {
error(404, 'Not Found');
}
src/routes/marx-brothers/[...path]/+page.ts
ts
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = (event) => {
error(404, 'Not Found');
};

如果您不处理 404 案例,它们将出现在 handleError

可选参数

[lang]/home 这样的路由包含一个名为 lang 的必需参数。有时,将这些参数设为可选是有益的,这样在该示例中,homeen/home 都指向同一页面。你可以通过将参数包装在另一对括号中来实现:[[lang]]/home

请注意,可选路由参数不能跟随 rest 参数([...rest]/[[optional]]),因为参数是“贪婪”匹配的,并且可选参数将始终未使用。

匹配

src/routes/archive/[page] 这样的路由将匹配 /archive/3,但它也将匹配 /archive/potato。我们不希望这样。你可以通过添加一个匹配器来确保路由参数格式正确——它获取参数字符串("3""potato")并在其有效时返回 true——到你的 params 目录...

src/params/integer.js
ts
/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
return /^\d+$/.test(param);
}
src/params/integer.ts
ts
import type { ParamMatcher } from '@sveltejs/kit';
export const match: ParamMatcher = (param) => {
return /^\d+$/.test(param);
};

...并扩充你的路由

src/routes/archive/[page]
src/routes/archive/[page=integer]

如果路径名不匹配,SvelteKit 将尝试匹配其他路由(使用下面指定的排序顺序),然后再最终返回 404。

params 目录中的每个模块都对应一个匹配器,但 *.test.js*.spec.js 文件除外,这些文件可用于对你的匹配器进行单元测试。

匹配器在服务器和浏览器上都运行。

排序

多个路由有可能匹配给定的路径。例如,以下每个路由都将匹配 /foo-abc

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 需要知道请求的是哪个路由。为此,它根据以下规则对它们进行排序...

  • 更具体的路由具有更高的优先级(例如,没有参数的路由比具有一个动态参数的路由更具体,依此类推)
  • 具有 匹配器[name=type])的参数比没有匹配器的参数([name])具有更高的优先级
  • [[optional]][...rest] 参数将被忽略,除非它们是路由的最后一部分,在这种情况下,它们将被视为优先级最低。换句话说,x/[[y]]/z 在排序时与 x/z 等效
  • 平局按字母顺序解决

...导致此排序,这意味着 /foo-abc 将调用 src/routes/foo-abc/+page.svelte,而 /foo-def 将调用 src/routes/foo-[c]/+page.svelte 而不是不那么具体的路由

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 确定字符的十六进制代码

ts
':'.charCodeAt(0).toString(16); // '3a', hence '[x+3a]'

你还可以使用 Unicode 转义序列。通常你不需要这样做,因为你可以直接使用未编码的字符,但如果你出于某种原因不能在文件名中包含表情符号,则可以使用转义字符。换句话说,这些是等效的

src/routes/[u+d83e][u+dd2a]/+page.svelte
src/routes/🤪/+page.svelte

Unicode 转义序列的格式为 [u+nnnn],其中 nnnn000010ffff 之间的一个有效值。(与 JavaScript 字符串转义不同,无需使用代理对来表示高于 ffff 的代码点。)要了解有关 Unicode 编码的更多信息,请参阅 使用 Unicode 进行编程

由于 TypeScript 难以处理. 字符开头的目录,因此在创建例如 .well-known 路由时,你可能会发现对这些字符进行编码很有用:src/routes/[x+2e]well-known/...

高级布局

默认情况下,布局层次结构反映了路由层次结构。在某些情况下,这可能不是你想要的。

(组)

也许你有一些“应用”路由,它们应该具有一个布局(例如 /dashboard/item),而其他一些是“营销”路由,它们应该具有一个不同的布局(/about/testimonials)。我们可以将这些路由与一个名称用括号括起来的目录分组——与普通目录不同,(app)(marketing) 不会影响其内部路由的 URL 路径名

src/routes/
│ (app)/
│ ├ dashboard/
│ ├ item/
│ └ +layout.svelte
│ (marketing)/
│ ├ about/
│ ├ testimonials/
│ └ +layout.svelte
├ admin/
└ +layout.svelte

你还可以将 +page 直接放在 (group) 中,例如,如果 / 应该是一个 (app)(marketing) 页面。

退出布局

根布局适用于你的应用的每个页面——如果省略,则默认为 <slot />。如果你希望某些页面具有与其他页面不同的布局层次结构,那么你可以将你的整个应用放在一个或多个组中,除了不应该继承公共布局的路由。

在上面的示例中,/admin 路由不会继承 (app)(marketing) 布局。

+page@

页面可以在逐个路由的基础上退出当前的布局层次结构。假设我们在前一个示例的 (app) 组中有一个 /item/[id]/embed 路由

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 继承
  • [email protected] - 从 src/routes/(app)/item/+layout.svelte 继承
  • +page@(app).svelte - 从 src/routes/(app)/+layout.svelte 继承
  • [email protected] - 从 src/routes/+layout.svelte 继承
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page@(app).svelte
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte

+layout@

与页面一样,布局本身可以使用相同的技术退出其父布局层次结构。例如,一个 [email protected] 组件将重置其所有子路由的层次结构。

src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte  // uses (app)/item/[id]/+layout.svelte
│ │ │ ├ +layout.svelte  // inherits from (app)/item/[email protected]
│ │ │ └ +page.svelte    // uses (app)/item/[email protected]
│ │ └ [email protected]   // inherits from root layout, skipping (app)/+layout.svelte
│ └ +layout.svelte
└ +layout.svelte

何时使用布局组

并非所有用例都适合布局分组,你也不应该觉得自己必须使用它们。你的用例可能会导致复杂的 (group) 嵌套,或者你不想为一个单一的异常值引入一个 (group)。完全可以使用其他方法,例如组合(可重用的 load 函数或 Svelte 组件)或 if 语句来实现你的目标。以下示例显示了一个倒回到根布局的布局,并重复使用了其他布局也可以使用的组件和函数

src/routes/nested/route/[email protected]
<script>
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	export let data;
</script>

<ReusableLayout {data}>
	<slot />
</ReusableLayout>
src/routes/nested/route/[email protected]
<script lang="ts">
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	export let data;
</script>

<ReusableLayout {data}>
	<slot />
</ReusableLayout>
src/routes/nested/route/+layout.js
ts
import { reusableLoad } from '$lib/reusable-load-function';
/** @type {import('./$types').PageLoad} */
export function load(event) {
// Add additional logic here, if needed
return reusableLoad(event);
}
src/routes/nested/route/+layout.ts
ts
import { reusableLoad } from '$lib/reusable-load-function';
import type { PageLoad } from './$types';
export const load: PageLoad = (event) => {
// Add additional logic here, if needed
return reusableLoad(event);
};

延伸阅读

上一个 编写适配器
下一个 钩子