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.svelteimport { 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.
error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function function load(event: any): voidload(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.
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.
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.
error(404, 'Not Found');
};[注意] 如果您不处理 404 情况,它们将在
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 目录...
/**
* @param {string} param
* @return {param is ('apple' | 'orange')}
* @satisfies {import('@sveltejs/kit').ParamMatcher}
*/
export function function match(param: any): booleanmatch(param: anyparam) {
return param: anyparam === 'apple' || param: anyparam === 'orange';
}import type { type ParamMatcher = (param: string) => booleanThe 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) => booleanThe 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.svelteSvelteKit 需要知道正在请求哪个路由。为了做到这一点,它根据以下规则对它们进行排序...
- 更具体的路由优先级更高(例如,没有参数的路由比有一个动态参数的路由更具体,依此类推)
- 参数带有匹配器(
[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): numberReturns the Unicode value of the character at the specified location.
charCodeAt(0).Number.toString(radix?: number): stringReturns a string representation of an object.
toString(16); // '3a', hence '[x+3a]'您也可以使用 Unicode 转义序列。通常情况下,您不需要这样做,因为您可以直接使用未编码的字符,但如果——出于某种原因——您不能使用包含表情符号的文件名,例如,那么您可以使用转义字符。换句话说,这些是等效的:
src/routes/[u+d83e][u+dd2a]/+page.svelte
src/routes/🤪/+page.svelteUnicode 转义序列的格式为[u+nnnn],其中nnnn是介于0000和10ffff之间的有效值。(与 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 语句来实现你的目标是完全可行的。以下示例显示了一个布局,它回滚到根布局并重用其他布局也可以使用的组件和函数:
<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>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>>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