Skip to main content

高级路由

剩余参数

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

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

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

{
	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/karlmarx-brothers/+error.svelte 文件将不会被渲染,因为没有匹配到路由。如果您想渲染嵌套的错误页面,您应该创建一个匹配任何 /marx-brothers/* 请求的路由,并从中返回 404:

src/routes/
├ marx-brothers/
| ├ [...path]/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
src/routes/marx-brothers/[...path]/+page
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */ export function function load(event: any): void
@type{import('./$types').PageLoad}
load
(event: anyevent) {
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not Found');
}
import { function error(status: number, body: App.Error): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
} from '@sveltejs/kit';
import type {
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
} from './$types';
export const const load: PageLoadload:
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
= (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) => {
function error(status: number, body?: {
    message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)

Throws an error with a HTTP status code and an optional message. When called during request handling, this will cause SvelteKit to return an error response without invoking handleError. Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(404, 'Not Found');
};

如果您不处理 404 情况,它们将出现在 handleError

可选参数

[lang]/home 这样的路由包含一个名为 lang 的必需参数。有时需要让这些参数成为可选的,这样在这个例子中 homeen/home 都指向同一个页面。您可以通过将参数包裹在另一对括号中来实现:[[lang]]/home

注意,可选路由参数不能跟在剩余参数后面([...rest]/[[optional]]),因为参数是”贪婪”匹配的,可选参数永远不会被使用。

匹配

src/routes/fruits/[page] 这样的路由会匹配 /fruits/apple,但它也会匹配 /fruits/rocketship。我们不希望这样。您可以通过在 params 目录中添加一个 匹配器 — 它接收参数字符串("apple""rocketship")并在有效时返回 true — 来确保路由参数格式正确...

src/params/fruit
/**
 * @param {string} param
 * @return {param is ('apple' | 'orange')}
 * @satisfies {import('@sveltejs/kit').ParamMatcher}
 */
export function function match(param: any): boolean
@paramparam
@return@satisfies{import('@sveltejs/kit').ParamMatcher}
match
(param: any
@paramparam
param
) {
return param: any
@paramparam
param
=== 'apple' || param: any
@paramparam
param
=== 'orange';
}
import type { type ParamMatcher = (param: string) => boolean

The shape of a param matcher. See matching for more info.

ParamMatcher
} from '@sveltejs/kit';
export const const match: (param: string) => param is ("apple" | "orange")match = ((param: stringparam: string): param: stringparam is ('apple' | 'orange') => { return param: stringparam === 'apple' || param: stringparam === 'orange'; }) satisfies type ParamMatcher = (param: string) => boolean

The shape of a param matcher. See matching for more info.

ParamMatcher
;

...并增强您的路由:

src/routes/fruits/[page=fruit]

如果路径名不匹配,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 确定字符的十六进制代码:

':'.String.charCodeAt(index: number): number

Returns the Unicode value of the character at the specified location.

@paramindex The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.
charCodeAt
(0).Number.toString(radix?: number): string

Returns a string representation of an object.

@paramradix Specifies a radix for converting numeric values to strings. This value is only used for numbers.
toString
(16); // '3a',因此使用 '[x+3a]'

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

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

Unicode 转义序列的格式是 [u+nnnn],其中 nnnn 是介于 000010ffff 之间的有效值。(与 JavaScript 字符串转义不同,不需要使用代理对来表示超过 ffff 的代码点。)要了解更多关于 Unicode 编码的信息,请参考 使用 Unicode 编程

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

高级布局

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

(group)

也许您有一些路由是”应用”路由,应该有一个布局(例如 /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) 页面。

跳出布局

根布局适用于您的应用的每个页面 — 如果省略,它默认为 {@render children()}。如果您希望某些页面有不同于其余页面的布局层次结构,那么您可以将整个应用放在一个或多个组内,除了 不应继承公共布局的路由。

在上面的例子中,/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
  • +page@item.svelte - 继承自 src/routes/(app)/item/+layout.svelte
  • +page@(app).svelte - 继承自 src/routes/(app)/+layout.svelte
  • +page@.svelte - 继承自 src/routes/+layout.svelte
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page@(app).svelte
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte

+layout@

像页面一样,布局本身也可以使用相同的方式跳出其父布局层次结构。例如,+layout@.svelte 组件将将重置其所有子路由的层次结构。

src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte  // 使用 (app)/item/[id]/+layout.svelte
│ │ │ ├ +layout.svelte  // 继承自 (app)/item/+layout@.svelte
│ │ │ └ +page.svelte    // 使用 (app)/item/+layout@.svelte
│ │ └ +layout@.svelte   // 继承自根布局,跳过 (app)/+layout.svelte
│ └ +layout.svelte
└ +layout.svelte

何时使用布局分组

并非所有用例都适合使用布局分组,您也不应该觉得必须使用它们。可能您的用例会导致复杂的 (group) 嵌套,或者您不想为一个特例引入 (group)。使用其他方式如组合(可复用的 load 函数或 Svelte 组件)或 if 语句来实现您想要的效果也完全可以。以下示例展示了一个回退到根布局并复用其他布局也可以使用的组件和函数:

src/routes/nested/route/+layout@
<script>
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	let { data, children } = $props();
</script>

<ReusableLayout {data}>
	{@render children()}
</ReusableLayout>
<script lang="ts">
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	let { data, children } = $props();
</script>

<ReusableLayout {data}>
	{@render children()}
</ReusableLayout>
src/routes/nested/route/+layout
import { function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad } from '$lib/reusable-load-function';

/** @type {import('./$types').PageLoad} */
export function function load(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageLoad}
load
(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) {
// Add additional logic here, if needed return function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event); }
import { function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad } from '$lib/reusable-load-function';
import type { type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageLoad } from './$types';

export const const load: PageLoadload: type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event) => {
	// Add additional logic here, if needed
	return function reusableLoad(event: import("@sveltejs/kit").LoadEvent): Promise<Record<string, any>>reusableLoad(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>event);
};

进一步阅读

在 GitHub 编辑此页面

上一页 下一页