iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0
Modern Web

Svelte 的奇妙冒險系列 第 26

[Svelte 的奇妙冒險] Day 26 - 進階路由

  • 分享至 

  • xImage
  •  

之前在 Day19 最後的總結有提到,頁面基本上都會按照它的檔案路徑而產生路由規則及使用對應 layout ,像是如果我的檔案是 /foo/+page.svelte 那原則上我會產生一個 URL 為 /foo 的頁面,且會使用到 /foo 以及 /+layout.svelte (如果有的話)

但 SvelteKit 還是有提供一些特殊規則可以讓我們跳脫這些限制。

route group

如果我今天想讓有兩個頁面想讓他們有共用的樣式以及驗證機制,當然我們可以直接將它們放在同一個資料夾裡然後再用 +layout.svelte+layout.server.ts 達成這個功能。

但有時候我們就是繼續保持 URL 的語意,不想因為 layout 的共用影響到 URL 的階層,這時可以用 (route) 來協助我們共用 layout 但不會影響到 URL 的顯示。

以官方教學文件的例子來看,有兩個頁面想要登入後才能被查看分別是 /app/account ,原本如果我想要共用 layout 我只能額外多一個 folder 存放他們 像是 authed/appauthed/account ,但如果改用 (authed) 後 authed 就不會出現在 URL 上了。

所以我們先新增兩個 +page.svelte

<!-- in day26/(authed)/account/+page.svelte -->
<h1>Account Page</h1>

<!-- in day26/(authed)/account/+page.svelte -->
<h1>APP Page</h1>

然後再新增要共用的 +layout.svelte+layout.sever.ts

<!-- in day26/(authed)/+layout.svelte -->

<slot />

<form method="POST" action="/day26/logout" class="mt-12">
	<button class="btn btn-warning btn-outline">log out</button>
</form>

action="/day26/logout 指的是這個 form submit 時是去觸發 /day26/logout/+page.server.ts 的 default action

// in day26/(authed)/+layout.sever.ts
import { redirect, type ServerLoad } from '@sveltejs/kit';

export const load: ServerLoad = ({ cookies, url }) => {
	if (!cookies.get('logged_in')) {
		throw redirect(303, `/day26/login?redirectTo=${url.pathname}`);
	}
};

<!-- in day26/+layout.svelte -->

<nav class="mb-4">
	<a href="/day26/account">account</a>
	<a href="/day26/app">app</a>
</nav>

<slot />

然後實作登入及登出的部分

<!-- in day26/login/+page.svelte -->

<form method="POST">
	<button class="btn btn-primary btn-outline">log in</button>
</form>

// in day26/login/+page.svelte.sever.ts

import { redirect } from '@sveltejs/kit';
import type { Actions } from './$types';

export const actions: Actions = {
	default: ({ cookies, url }) => {
		cookies.set('logged_in', 'true', { path: '/' });
		throw redirect(303, url.searchParams.get('redirectTo') ?? '/day26/account');
	}
};

// in day26/logout/+page.svelte.sever.ts

import { redirect } from '@sveltejs/kit';

export const actions = {
	default: ({ cookies }) => {
		cookies.delete('logged_in', { path: '/' });
		throw redirect(303, '/day26/login');
	}
};

可以發現當我在 Account 頁面時 URL 是顯示 http://localhost:5173/day26/account

+page@.svelte

那如果我們是想要特定的頁面中不要繼承 parent 的 layout,就可以使用 +page@.svelte 來讓這個頁面的佈局重置

以剛剛了例子來說,我在 day26/+layout.svelte 新增了一個 nav 來 redirect 頁面,但如果現在想要讓它不要出現在 login 頁面的話,可以將 day26/login/+page.svelte 改為 day26/login/+page@.svelte ,這樣子就不會使用到 day26/+layout.svelte

另外值得提的是 @ 是可以選擇要在哪個路由重置,假設我今天多了一個路由是 day26/(authed)/app/setting/+page.svelte 以及多了一個 day26/(authed)/app/+layout.svelte

<!-- day26/(authed)/app/setting/+page.svelte -->
<h1>Setting</h1>
<!-- day26/(authed)/app/+layout.svelte -->

<nav class="mb-4">
	<a href="/day26/app/setting">setting</a>
</nav>

<slot />

預設情況下會繼承 routes/+layout.svelteroutes/day26/+layout.svelteroutes/day26/(authed)/+layout.svelte 以及 routes/day26/(authed)/app/+layout.svelte

如果今天只想顯示 routes/+layout.svelte 的我就是把檔名改成 +page@.svelte ,意即將這個 page 視為是在 root 的路徑,以此類推

  1. 如果我想顯示routes/+layout.svelteroutes/day26/+layout.svelte 就是將檔名改為 +page@day26.svelte

  2. 如果我想顯示routes/+layout.svelteroutes/day26/+layout.svelteroutes/day26/(authed)/+layout.svelte 就是將檔名改為 +page@(authed).svelte

+page.svelte

+page@(authed).svelte

+page@day26.svelte

+page@.svelte

所以唯一的限制就是 routes/+layout.svelte 一定會被用到,因為 @ 就是視為放到 routes 資料夾也就跟 routes/+layout.svelte 同層了。


參考資料

source code

demo 站點


上一篇
[Svelte 的奇妙冒險] Day 25 - Shallow routing
下一篇
[Svelte 的奇妙冒險] Day 27 - 環境變數
系列文
Svelte 的奇妙冒險30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言