之前在 Day19 最後的總結有提到,頁面基本上都會按照它的檔案路徑而產生路由規則及使用對應 layout ,像是如果我的檔案是 /foo/+page.svelte
那原則上我會產生一個 URL 為 /foo
的頁面,且會使用到 /foo
以及 /
的 +layout.svelte
(如果有的話)
但 SvelteKit 還是有提供一些特殊規則可以讓我們跳脫這些限制。
如果我今天想讓有兩個頁面想讓他們有共用的樣式以及驗證機制,當然我們可以直接將它們放在同一個資料夾裡然後再用 +layout.svelte
及 +layout.server.ts
達成這個功能。
但有時候我們就是繼續保持 URL 的語意,不想因為 layout 的共用影響到 URL 的階層,這時可以用 (route)
來協助我們共用 layout 但不會影響到 URL 的顯示。
以官方教學文件的例子來看,有兩個頁面想要登入後才能被查看分別是 /app
和 /account
,原本如果我想要共用 layout 我只能額外多一個 folder 存放他們 像是 authed/app
及 authed/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
那如果我們是想要特定的頁面中不要繼承 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.svelte
、 routes/day26/+layout.svelte
、 routes/day26/(authed)/+layout.svelte
以及 routes/day26/(authed)/app/+layout.svelte
如果今天只想顯示 routes/+layout.svelte
的我就是把檔名改成 +page@.svelte
,意即將這個 page 視為是在 root 的路徑,以此類推
如果我想顯示routes/+layout.svelte
、 routes/day26/+layout.svelte
就是將檔名改為 +page@day26.svelte
如果我想顯示routes/+layout.svelte
、 routes/day26/+layout.svelte
、 routes/day26/(authed)/+layout.svelte
就是將檔名改為 +page@(authed).svelte
+page.svelte
+page@(authed).svelte
+page@day26.svelte
+page@.svelte
所以唯一的限制就是 routes/+layout.svelte
一定會被用到,因為 @
就是視為放到 routes
資料夾也就跟 routes/+layout.svelte
同層了。