跳至主要內容

進階

進階路由

在 GitHub 上編輯此頁面

Rest 參數

如果路由區段數目未知,您可以使用 rest 語法 — 例如,您可以這樣實作 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 等。請務必檢查 rest 參數的值是否有效,例如使用 比對器

404 頁面

Rest 參數也允許您呈現自訂 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.js
ts
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function load(event) {
error(404, 'Not Found');
}
src/routes/marx-brothers/[...path]/+page.ts
ts
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = (event) => {
error(404, 'Not Found');
};

如果您不處理 404 案例,它們將出現在 handleError

選用參數

[lang]/home 的路由包含一個名為 lang 的必要參數。有時讓這些參數成為選用參數是有益的,因此在這個範例中 homeen/home 都指向同一個頁面。您可以透過在另一個方括號對中包裝參數來做到這一點:[[lang]]/home

請注意,選用路由參數不能出現在 rest 參數 ([...rest]/[[optional]]) 之後,因為參數是「貪婪地」匹配,而且選用參數永遠不會被使用。

匹配

src/routes/archive/[page] 的路由會匹配 /archive/3,但也會匹配 /archive/potato。我們不想要這樣。您可以透過新增一個「比對器」來確保路由參數格式正確,這個比對器會取得參數字串 ("3""potato") 並傳回 true (如果有效) 到您的 params 目錄...

src/params/integer.js
ts
/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
return /^\d+$/.test(param);
}
src/params/integer.ts
ts
import type { ParamMatcher } from '@sveltejs/kit';
export const match: 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],其中 nnnn000010ffff 之間的有效值。(與 JavaScript 字串跳脫不同,不需要使用代理對來表示 ffff 以上的碼點。)若要深入了解 Unicode 編碼,請參閱 使用 Unicode 進行程式設計

由於 TypeScript 處理字首為 . 字元的目錄時會遇到困難,因此您可能會發現將這些字元編碼為 e.g. .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
  • +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@.svelte
<script>
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	export let data;
</script>

<ReusableLayout {data}>
	<slot />
</ReusableLayout>
src/routes/nested/route/+layout@.svelte
<script lang="ts">
	import ReusableLayout from '$lib/ReusableLayout.svelte';
	export let data;
</script>

<ReusableLayout {data}>
	<slot />
</ReusableLayout>
src/routes/nested/route/+layout.js
ts
import { reusableLoad } from '$lib/reusable-load-function';
/** @type {import('./$types').PageLoad} */
export function load(event) {
// Add additional logic here, if needed
return reusableLoad(event);
}
src/routes/nested/route/+layout.ts
ts
import { reusableLoad } from '$lib/reusable-load-function';
import type { PageLoad } from './$types';
export const load: PageLoad = (event) => {
// Add additional logic here, if needed
return reusableLoad(event);
};

延伸閱讀

上一個 撰寫轉接器
下一個 鉤子