跳至主要内容

高级路由

剩余参数

如果路由段的数量未知,可以使用剩余语法 - 例如,你可以像这样实现 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/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
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。我们不希望这样。你可以通过添加一个匹配器来确保路由参数格式正确 - 它接收参数字符串("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])的参数
  • [[optional]][...rest] 参数会被忽略,除非它们是路由的最后一部分,在这种情况下,它们会被视为最低优先级。换句话说,对于排序的目的,x/[[y]]/zx/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],其中 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) 页面。

跳出布局

根布局应用于你的应用的每个页面 - 如果省略,则默认为 {@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 继承
  • [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/+layout@
<script>
	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 上编辑此页面

上一页 下一页