高级
高级路由
在 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
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').PageLoad} */export functionload (event ) {error (404, 'Not Found');}
ts
import {error } from '@sveltejs/kit';import type {PageLoad } from './$types';export constload :PageLoad = (event ) => {error (404, 'Not Found');};
如果您不处理 404 案例,它们将出现在
handleError
中
可选参数永久链接
像 [lang]/home
这样的路由包含一个名为 lang
的必需参数。有时,将这些参数设为可选是有益的,这样在该示例中,home
和 en/home
都指向同一页面。你可以通过将参数包装在另一对括号中来实现:[[lang]]/home
请注意,可选路由参数不能跟随 rest 参数([...rest]/[[optional]]
),因为参数是“贪婪”匹配的,并且可选参数将始终未使用。
匹配永久链接
像 src/routes/archive/[page]
这样的路由将匹配 /archive/3
,但它也将匹配 /archive/potato
。我们不希望这样。你可以通过添加一个匹配器来确保路由参数格式正确——它获取参数字符串("3"
或 "potato"
)并在其有效时返回 true
——到你的 params
目录...
ts
/** @type {import('@sveltejs/kit').ParamMatcher} */export functionmatch (param ) {return /^\d+$/.test (param );}
ts
import type {ParamMatcher } from '@sveltejs/kit';export constmatch :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]
,其中 nnnn
是 0000
和 10ffff
之间的一个有效值。(与 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 语句来实现你的目标。以下示例显示了一个倒回到根布局的布局,并重复使用了其他布局也可以使用的组件和函数
<script>
import ReusableLayout from '$lib/ReusableLayout.svelte';
export let data;
</script>
<ReusableLayout {data}>
<slot />
</ReusableLayout>
<script lang="ts">
import ReusableLayout from '$lib/ReusableLayout.svelte';
export let data;
</script>
<ReusableLayout {data}>
<slot />
</ReusableLayout>
ts
import {reusableLoad } from '$lib/reusable-load-function';/** @type {import('./$types').PageLoad} */export functionload (event ) {// Add additional logic here, if neededreturnreusableLoad (event );}
ts
import {reusableLoad } from '$lib/reusable-load-function';import type {PageLoad } from './$types';export constload :PageLoad = (event ) => {// Add additional logic here, if neededreturnreusableLoad (event );};