Skip to main content

Advanced routing

剩余参数

如果路由段的数量未知,您可以使用剩余语法 - 例如,您可能可以这样实现 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/+error.svelte 将不会被渲染,如果您访问 /marx-brothers/karl,因为没有匹配到路由。如果您想渲染嵌套的错误页面,您应该创建一个匹配任何 /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 参数之后([...rest]/[[optional]]),因为参数是“贪婪”匹配的,可选参数将始终未被使用。

匹配

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

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])的参数
  • [[可选]][...其余] 参数除非是路由的最后一部分,否则将被忽略,在这种情况下,它们将被赋予最低优先级。换句话说,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', hence '[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/...

高级布局

默认情况下,布局层次结构路由层次结构相匹配。在某些情况下,这可能不是您想要的结果。

(組)

可能您有一些路由是 ‘app’ 路由,应该使用一个布局(例如 /dashboard/item),而其他的是 ‘marketing’ 路由,应该使用不同的布局(/about/testimonials)。我们可以将这些路由分组到一个以括号包裹的目录中——与普通目录不同,(app)(marketing) 不会影响它们内部路由的 URL 路径名:

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 路由:

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@.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 语句来实现你的目标是完全可行的。以下示例显示了一个布局,它回滚到根布局并重用其他布局也可以使用的组件和函数:

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);
};

进一步阅读

Edit this page on GitHub llms.txt

previous next