跳至主要內容

核心概念

頁面選項

在 GitHub 上編輯此頁面

預設情況下,SvelteKit 會在伺服器上先渲染(或預渲染)任何元件,並將其作為 HTML 傳送給用戶端。然後,它會在瀏覽器中再次渲染元件,以使其在稱為水化的過程中具有互動性。因此,您需要確保元件可以在兩個地方執行。然後,SvelteKit 將初始化一個路由器,以接管後續導覽。

您可以透過從+page.js+page.server.js匯出選項,或使用共用+layout.js+layout.server.js為多個頁面群組來控制每個頁面的這些選項。若要定義整個應用程式的選項,請從根佈局匯出它。子佈局和頁面會覆寫父佈局中設定的值,因此 — 例如 — 您可以在整個應用程式中啟用預渲染,然後針對需要動態渲染的頁面停用它。

您可以在應用程式的不同區域混合並搭配這些選項。例如,您可以預渲染您的行銷頁面以達到最高速度,伺服器渲染您的動態頁面以進行 SEO 和無障礙性,並透過僅在用戶端渲染您的管理員區段將其轉換為 SPA。這使得 SvelteKit 非常靈活。

預渲染

您的應用程式中至少有一些路由很可能可以表示為在建置時產生的簡單 HTML 檔案。這些路由可以預渲染

+page.js/+page.server.js/+server.js
ts
export const prerender = true;
+page.ts/+page.server.js/+server.js
ts
export const prerender = true;

或者,您可以在根目錄的 +layout.js+layout.server.js 中設定 export const prerender = true,並預渲染除了明確標示為可預渲染的頁面之外的所有內容

+page.js/+page.server.js/+server.js
ts
export const prerender = false;
+page.ts/+page.server.js/+server.js
ts
export const prerender = false;

具有 prerender = true 的路徑會從用於動態 SSR 的清單中排除,讓您的伺服器(或無伺服器/邊緣函式)變小。在某些情況下,您可能想要預先渲染路徑,但也要將其包含在清單中(例如,使用 /blog/[slug] 之類的路徑,您希望預先渲染最新/最受歡迎的內容,但伺服器渲染長尾)— 對於這些情況,有一個第三個選項,'auto'

+page.js/+page.server.js/+server.js
ts
export const prerender = 'auto';
+page.ts/+page.server.js/+server.js
ts
export const prerender = 'auto';

如果您的整個應用程式適合預先渲染,您可以使用 adapter-static,它將輸出適合與任何靜態網路伺服器一起使用的檔案。

預先渲染器將從應用程式的根目錄開始,並為任何可預先渲染的頁面或它找到的 +server.js 路徑產生檔案。每個頁面都會掃描指向其他可預先渲染頁面的 <a> 元素 — 因此,您通常不需要指定應存取哪些頁面。如果您確實需要指定預先渲染器應存取哪些頁面,您可以使用 config.kit.prerender.entries 來執行此操作,或從您的動態路徑匯出 entries 函式。

在預先渲染時,從 $app/environment 匯入的 building 值將為 true

預先渲染伺服器路徑

與其他頁面選項不同,prerender 也適用於 +server.js 檔案。這些檔案不受版面配置影響,但會繼承從它們擷取資料的頁面的預設值(如果有的話)。例如,如果 +page.js 包含這個 load 函式...

+page.js
ts
export const prerender = true;
/** @type {import('./$types').PageLoad} */
export async function load({ fetch }) {
const res = await fetch('/my-server-route.json');
return await res.json();
}
+page.ts
ts
import type { PageLoad } from './$types';
export const prerender = true;
export const load: PageLoad = async ({ fetch }) => {
const res = await fetch('/my-server-route.json');
return await res.json();
};

...那麼 src/routes/my-server-route.json/+server.js 將被視為可預先渲染,如果它不包含自己的 export const prerender = false

何時不預先渲染

基本規則是:對於一個可預先渲染的頁面,任何兩個直接點選它的使用者都必須從伺服器取得相同的內容。

並非所有頁面都適合預先渲染。任何預先渲染的內容都將被所有使用者看到。您當然可以在預先渲染的頁面中 onMount 中擷取個人化資料,但這可能會導致較差的使用者體驗,因為它會涉及空白的初始內容或載入指標。

請注意,您仍然可以預先渲染根據頁面參數載入資料的頁面,例如 src/routes/blog/[slug]/+page.svelte 路徑。

預先渲染期間禁止存取 url.searchParams。如果您需要使用它,請確保您只在瀏覽器中執行此操作(例如在 onMount 中)。

具有 動作 的頁面無法預先渲染,因為伺服器必須能夠處理動作 POST 要求。

路由衝突

由於預先渲染會寫入檔案系統,因此不可能有兩個端點會導致目錄和檔案具有相同名稱。例如,src/routes/foo/+server.jssrc/routes/foo/bar/+server.js 會嘗試建立 foofoo/bar,這是不可行的。

基於此原因和其他原因,建議您始終包含檔案副檔名 — src/routes/foo.json/+server.jssrc/routes/foo/bar.json/+server.js 將產生 foo.jsonfoo/bar.json 檔案,並和諧地並排存在。

對於頁面,我們透過撰寫 foo/index.html 而不是 foo 來迴避這個問題。

疑難排解

如果您遇到類似「以下路由標記為可預先渲染,但未預先渲染」的錯誤,這是因為有問題的路由(或父層配置,如果是頁面)具有 export const prerender = true,但頁面實際上未預先渲染,因為預先渲染爬蟲未到達該頁面。

由於這些路由無法動態伺服器端渲染,因此當人們嘗試存取有問題的路由時,這將導致錯誤。有兩種方法可以解決此問題

  • 透過遵循 config.kit.prerender.entriesentries 頁面選項中的連結,確保 SvelteKit 可以找到路由。如果無法透過爬取其他進入點找到動態路由(即具有 [parameters] 的頁面),請將連結新增到此選項,否則它們不會被預先渲染,因為 SvelteKit 不知道參數應具有什麼值。未標記為可預先渲染的頁面將被忽略,而且即使其中一些頁面可預先渲染,它們連結到其他頁面的連結也不會被爬取。
  • export const prerender = true 變更為 export const prerender = 'auto'。具有 'auto' 的路由可以動態伺服器端渲染

entries

SvelteKit 會自動從進入點開始並爬取它們,以找出要預先渲染的頁面。預設情況下,所有非動態路由都被視為進入點 — 例如,如果您有這些路由...

/             # non-dynamic
/blog         # non-dynamic
/blog/[slug]  # dynamic, because of `[slug]`

...SvelteKit 會預先渲染 //blog,並在過程中找出類似 <a href="/blog/hello-world"> 的連結,這會提供新的頁面供其預先渲染。

在大部分情況下,這就足夠了。在某些情況下,連結到類似 /blog/hello-world 的頁面可能不存在(或可能不存在於預先渲染的頁面中),在這種情況下,我們需要告訴 SvelteKit 它們的存在。

這可以使用 config.kit.prerender.entries 來完成,或透過從屬於動態路由的 +page.js+page.server.js+server.js 匯出 entries 函數

src/routes/blog/[slug]/+page.server.js
ts
/** @type {import('./$types').EntryGenerator} */
export function entries() {
return [
{ slug: 'hello-world' },
{ slug: 'another-blog-post' }
];
}
export const prerender = true;
src/routes/blog/[slug]/+page.server.ts
ts
import type { EntryGenerator } from './$types';
export const entries: EntryGenerator = () => {
return [{ slug: 'hello-world' }, { slug: 'another-blog-post' }];
};
export const prerender = true;

entries 可以是一個 async 函式,讓你可以(例如)從 CMS 或資料庫中擷取一串文章,如上方的範例。

ssr

通常,SvelteKit 會先在伺服器上呈現你的頁面,然後將 HTML 傳送給用戶端,在那裡會進行 水化。如果你將 ssr 設為 false,它會呈現一個空的「外殼」頁面。如果你無法在伺服器上呈現你的頁面(因為你使用僅限瀏覽器的全域變數,例如 document),這會很有用,但大多數情況下不建議這麼做(請參閱附錄)。

+page.js
ts
export const ssr = false;
// If both `ssr` and `csr` are `false`, nothing will be rendered!
+page.ts
ts
export const ssr = false;
// If both `ssr` and `csr` are `false`, nothing will be rendered!

如果你將 export const ssr = false 加入你的根目錄 +layout.js,你的整個應用程式將只會在用戶端上呈現,這基本上表示你將你的應用程式變成單頁應用程式 (SPA)。

csr

通常,SvelteKit 會將你的伺服器呈現 HTML 水化 為互動式的用戶端呈現 (CSR) 頁面。有些頁面根本不需要 JavaScript,許多部落格文章和「關於」頁面都屬於此類別。在這些情況下,你可以停用 CSR

+page.js
ts
export const csr = false;
// If both `csr` and `ssr` are `false`, nothing will be rendered!
+page.ts
ts
export const csr = false;
// If both `csr` and `ssr` are `false`, nothing will be rendered!

停用 CSR 不會將任何 JavaScript 傳送給用戶端。這表示

  • 網頁應該只使用 HTML 和 CSS 運作。
  • 所有 Svelte 元件內的 <script> 標籤都會被移除。
  • <form> 元素無法 逐步增強
  • 連結會由瀏覽器以全頁導覽處理。

trailingSlash

預設情況下,SvelteKit 會從 URL 中移除尾斜線,如果你造訪 /about/,它會回應一個重新導向至 /about 的請求。你可以使用 trailingSlash 選項變更此行為,它可以是 'never'(預設值)、'always''ignore' 之一。

與其他頁面選項一樣,你可以從 +layout.js+layout.server.js 匯出此值,它會套用至所有子頁面。你也可以從 +server.js 檔案匯出組態。

src/routes/+layout.js
ts
export const trailingSlash = 'always';
src/routes/+layout.ts
ts
export const trailingSlash = 'always';

此選項也會影響預先渲染。如果 trailingSlashalways,則像 /about 的路由會產生 about/index.html 檔案,否則會建立 about.html,反映靜態 Web 伺服器慣例。

不建議忽略尾斜線 — 兩種情況下相對路徑的語意不同(從 /x./y/y,但從 /x/./y/x/y),而 /x/x/ 被視為不同的 URL,這對 SEO 有害。

config

透過適配器的概念,SvelteKit 能夠在各種平台上執行。每個平台可能都有特定的組態,用於進一步調整部署 — 例如在 Vercel 上,你可以選擇將應用程式的某些部分部署在邊緣,而其他部分部署在無伺服器環境中。

config 是頂層的鍵值對物件。除此之外,具體的形狀取決於你使用的適配器。每個適配器都應該提供 Config 介面以匯入類型安全性。請參閱適配器的文件以取得更多資訊。

src/routes/+page.js
ts
/** @type {import('some-adapter').Config} */
export const config = {
runtime: 'edge'
};
src/routes/+page.ts
ts
import type { Config } from 'some-adapter';
export const config: Config = {
runtime: 'edge',
};

config 物件會在頂層合併(但不是更深層級)。這表示如果你只想覆寫上層 +layout.js 中的部分值,則不需要在 +page.js 中重複所有值。例如這個版面組態...

src/routes/+layout.js
ts
export const config = {
runtime: 'edge',
regions: 'all',
foo: {
bar: true
}
}
src/routes/+layout.ts
ts
export const config = {
runtime: 'edge',
regions: 'all',
foo: {
bar: true,
},
};

...被這個頁面組態覆寫...

src/routes/+page.js
ts
export const config = {
regions: ['us1', 'us2'],
foo: {
baz: true
}
}
src/routes/+page.ts
ts
export const config = {
regions: ['us1', 'us2'],
foo: {
baz: true,
},
};

...這會產生該頁面的組態值 { runtime: 'edge', regions: ['us1', 'us2'], foo: { baz: true } }

進一步閱讀

上一個 表單動作
下一個 狀態管理