跳至主要內容

進階路由

剩餘參數

如果路由區段的數量未知,您可以使用剩餘語法 — 例如,您可能會像這樣實作 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。我們不希望這樣。您可以透過加入匹配器來確保路由參數格式正確 — 該匹配器會接收參數字串 ("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]]/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],其中 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 繼承
  • +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  // 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

何時使用版面配置群組

並非所有使用情境都適合使用版面分組,您也不應該覺得非用不可。有時候,您的使用情境可能會導致複雜的 (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 上編輯此頁面

上一頁 下一頁