核心概念
載入資料
在 GitHub 上編輯此頁面在 +page.svelte
元件(及其包含的 +layout.svelte
元件)可以被渲染之前,我們通常需要取得一些資料。這是透過定義 load
函式來完成的。
頁面資料永久連結
+page.svelte
檔案可以有一個兄弟檔 +page.js
,它會匯出一個 load
函式,其回傳值會透過 data
prop 提供給頁面
ts
/** @type {import('./$types').PageLoad} */export functionload ({params }) {return {post : {title : `Title for ${params .slug } goes here`,content : `Content for ${params .slug } goes here`}};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({params }) => {return {post : {title : `Title for ${params .slug } goes here`,content : `Content for ${params .slug } goes here`,},};};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
感謝產生的 $types
模組,我們得到了完整的類型安全性。
在 +page.js
檔案中的 load
函式會同時在伺服器和瀏覽器上執行(除非與 export const ssr = false
結合,否則它將 僅在瀏覽器中執行)。如果你的 load
函式應該總是執行於伺服器上(例如,因為它使用私有環境變數或存取資料庫),那麼它將會轉而使用 +page.server.js
。
一個更實際的部落格文章 load
函式版本,它只會執行於伺服器上並從資料庫中提取資料,可能如下所示
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
請注意,類型已從 PageLoad
變更為 PageServerLoad
,因為伺服器 load
函式可以存取其他引數。若要了解何時使用 +page.js
,何時使用 +page.server.js
,請參閱 通用與伺服器。
版面資料永久連結
您的 +layout.svelte
檔案也可以透過 +layout.js
或 +layout.server.js
載入資料。
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
從版面 load
函式傳回的資料可供子 +layout.svelte
元件和 +page.svelte
元件,以及它「所屬」的版面使用。
<script>
import { page } from '$app/stores';
/** @type {import('./$types').PageData} */
export let data;
// we can access `data.posts` because it's returned from
// the parent layout `load` function
$: index = data.posts.findIndex(post => post.slug === $page.params.slug);
$: next = data.posts[index - 1];
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#if next}
<p>Next post: <a href="/blog/{next.slug}">{next.title}</a></p>
{/if}
如果多個
load
函式傳回具有相同金鑰的資料,最後一個「獲勝」—版面load
傳回{ a: 1, b: 2 }
,而頁面load
傳回{ b: 3, c: 4 }
的結果將會是{ a: 1, b: 3, c: 4 }
。
$page.data永久連結
+page.svelte
元件,以及其上方的每個 +layout.svelte
元件,都可以存取其自己的資料,以及其父項目的所有資料。
在某些情況下,我們可能需要相反的情況—父版面可能需要存取頁面資料或子版面的資料。例如,根版面可能想要存取從 +page.js
或 +page.server.js
中的 load
函式傳回的 title
屬性。這可以使用 $page.data
來完成
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
<script lang="ts">
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
$page.data
的類型資訊由 App.PageData
提供。
通用與伺服器永久連結
正如我們所見,有兩種 load
函式
+page.js
和+layout.js
檔案會匯出在伺服器和瀏覽器上執行的通用load
函式+page.server.js
和+layout.server.js
檔案會匯出僅在伺服器端執行的伺服器load
函式
在概念上,它們是同一件事,但有一些重要的差異需要注意。
哪個載入函式在何時執行?永久連結
伺服器 load
函式總是在伺服器上執行。
預設情況下,通用 load
函式會在使用者第一次造訪您的頁面時,於 SSR 期間在伺服器上執行。然後,它們會在水化期間再次執行,重複使用來自 擷取請求 的任何回應。通用 load
函式的後續呼叫都會在瀏覽器中發生。您可以透過 頁面選項 自訂行為。如果您停用 伺服器端呈現,您將會取得 SPA,而通用 load
函式總是在客戶端執行。
如果路由同時包含通用和伺服器 load
函數,伺服器 load
會先執行。
load
函數會在執行階段呼叫,除非您 預先渲染 頁面,否則會在建置階段呼叫。
輸入永久連結
通用和伺服器 load
函數都可以存取描述請求的屬性(params
、route
和 url
)和各種函數(fetch
、setHeaders
、parent
、depends
和 untrack
)。這些屬性和函數會在以下各節中說明。
伺服器 load
函數會呼叫 ServerLoadEvent
,它會從 RequestEvent
繼承 clientAddress
、cookies
、locals
、platform
和 request
。
通用 load
函數會呼叫 LoadEvent
,它有一個 data
屬性。如果您在 +page.js
和 +page.server.js
(或 +layout.js
和 +layout.server.js
)中都有 load
函數,伺服器 load
函數的回傳值會是通用 load
函數參數的 data
屬性。
輸出永久連結
通用 load
函數可以回傳包含任何值的物件,包括自訂類別和元件建構函數。
伺服器 load
函數必須回傳可以用 devalue 序列化資料,也就是任何可以表示為 JSON 的資料,加上 BigInt
、Date
、Map
、Set
和 RegExp
,或重複/循環參照,以便透過網路傳輸。您的資料可以包含 承諾,如果包含,它會串流到瀏覽器。
何時使用哪一個永久連結
當您需要直接從資料庫或檔案系統存取資料,或需要使用私人環境變數時,伺服器 load
函數會很方便。
通用 load
函數在您需要從外部 API fetch
資料,且不需要私人憑證時很有用,因為 SvelteKit 可以直接從 API 取得資料,而不是透過您的伺服器。當您需要傳回無法序列化的內容時,例如 Svelte 元件建構函式,它們也很有用。
在少數情況下,您可能需要同時使用這兩個函數 — 例如,您可能需要傳回自訂類別的執行個體,而該執行個體已使用伺服器中的資料初始化。同時使用這兩個函數時,伺服器 load
傳回值不會直接傳遞給頁面,而是傳遞給通用 load
函數(作為 data
屬性)
ts
/** @type {import('./$types').PageServerLoad} */export async functionload () {return {serverMessage : 'hello from server load function'};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async () => {return {serverMessage : 'hello from server load function',};};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({data }) {return {serverMessage :data .serverMessage ,universalMessage : 'hello from universal load function'};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({data }) => {return {serverMessage :data .serverMessage ,universalMessage : 'hello from universal load function',};};
使用 URL 資料permalink
通常,load
函數會以某種方式取決於 URL。為此,load
函數會提供 url
、route
和 params
給您。
urlpermalink
URL
的執行個體,包含 origin
、hostname
、pathname
和 searchParams
等屬性(包含已剖析的查詢字串,為 URLSearchParams
物件)。url.hash
無法在 load
期間存取,因為它在伺服器上不可用。
在某些環境中,這會在伺服器端呈現期間從請求標頭中衍生而來。例如,如果您正在使用 adapter-node,您可能需要設定該轉接器,才能讓 URL 正確無誤。
routepermalink
包含目前路由目錄的名稱,相對於 src/routes
ts
/** @type {import('./$types').PageLoad} */export functionload ({route }) {console .log (route .id ); // '/a/[b]/[...c]'}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({route }) => {console .log (route .id ); // '/a/[b]/[...c]'};
paramspermalink
params
衍生自 url.pathname
和 route.id
。
假設 route.id
為 /a/[b]/[...c]
,而 url.pathname
為 /a/x/y/z
,則 params
物件會如下所示
ts
{"b": "x","c": "y/z"}
發出 fetch 請求permalink
要從外部 API 或 +server.js
處理常式取得資料,可以使用提供的 fetch
函式,其行為與 原生 fetch
網路 API 相同,但具備一些額外功能
- 它可用於在伺服器上進行憑證請求,因為它繼承了頁面請求的
cookie
和authorization
標頭。 - 它可以在伺服器上進行相對請求(通常,
fetch
在伺服器環境中使用時需要具有來源的 URL)。 - 內部請求(例如,對於
+server.js
路由)在伺服器上執行時會直接傳送至處理常式函式,而不會產生 HTTP 呼叫的負擔。 - 在伺服器端渲染期間,回應會被擷取並透過連接到
Response
物件的text
、json
和arrayBuffer
方法內嵌到已渲染的 HTML 中。請注意,標頭不會被序列化,除非透過filterSerializedResponseHeaders
明確包含。 - 在水化期間,回應會從 HTML 中讀取,保證一致性並防止額外的網路請求 - 如果你在使用瀏覽器
fetch
而不是load
fetch
時在瀏覽器主控台中收到警告,這就是原因。
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,params }) {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,params }) => {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };};
Cookie永久連結
伺服器 load
函式可以取得和設定 cookie
。
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload ({cookies }) {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid )};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async ({cookies }) => {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid ),};};
Cookie 僅會透過提供的 fetch
函式傳遞,如果目標主機與 SvelteKit 應用程式相同,或為其更明確的子網域。
例如,如果 SvelteKit 服務 my.domain.com
- domain.com 不會收到 cookie
- my.domain.com 會收到 cookie
- api.domain.com 不會收到 cookie
- sub.my.domain.com 會收到 cookie
當設定 credentials: 'include'
時,其他 cookie 也不會被傳遞,因為 SvelteKit 不知道每個 cookie 屬於哪個網域(瀏覽器不會傳遞此資訊),所以轉送任何 cookie 都不安全。使用 handleFetch 鉤子 來解決這個問題。
標頭永久連結
伺服器和通用 load
函式都可以使用 setHeaders
函式,當在伺服器上執行時,可以設定回應的標頭。(在瀏覽器中執行時,setHeaders
沒有作用。)這在您希望頁面被快取時很有用,例如
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,setHeaders }) {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// cache the page for the same length of time// as the underlying datasetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control')});returnresponse .json ();}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,setHeaders }) => {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// cache the page for the same length of time// as the underlying datasetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control'),});returnresponse .json ();};
多次設定相同的標頭(即使在不同的 load
函式中)是一個錯誤 - 您只能設定一個給定的標頭一次。您無法使用 setHeaders
新增 set-cookie
標頭 - 請改用 cookies.set(name, value, options)
。
使用父層資料永久連結
偶爾,load
函式需要存取來自父層 load
函式的資料,這可以使用 await parent()
來完成
ts
/** @type {import('./$types').LayoutLoad} */export functionload () {return {a : 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = () => {return {a : 1 };};
ts
/** @type {import('./$types').LayoutLoad} */export async functionload ({parent }) {const {a } = awaitparent ();return {b :a + 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = async ({parent }) => {const {a } = awaitparent ();return {b :a + 1 };};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({parent }) {const {a ,b } = awaitparent ();return {c :a +b };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({parent }) => {const {a ,b } = awaitparent ();return {c :a +b };};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
請注意,
+page.js
中的load
函式會接收合併來自兩個版面load
函式的資料,而不仅仅是直接的父層。
在 +page.server.js
和 +layout.server.js
內,parent
會傳回來自父層 +layout.server.js
檔案的資料。
在 +page.js
或 +layout.js
中,它會傳回來自父層 +layout.js
檔案的資料。但是,遺失的 +layout.js
會被視為 ({ data }) => data
函式,這表示它也會傳回來自父層 +layout.server.js
檔案的資料,這些檔案沒有被 +layout.js
檔案「遮蔽」
使用 await parent()
時,請小心不要引入瀑布效應。例如,這裡的 getData(params)
並不依賴於呼叫 parent()
的結果,所以我們應該先呼叫它以避免延遲渲染。
/** @type {import('./$types').PageLoad} */
export async function load({ params, parent }) {
const parentData = await parent();
const data = await getData(params);
const parentData = await parent();
return {
...data
meta: { ...parentData.meta, ...data.meta }
};
}
錯誤永久連結
如果在 load
期間引發錯誤,最近的 +error.svelte
會被渲染。對於 預期的 錯誤,請使用 @sveltejs/kit
中的 error
輔助函式來指定 HTTP 狀態碼和一個可選的訊息
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {error (401, 'not logged in');}if (!locals .user .isAdmin ) {error (403, 'not an admin');}}
ts
import {error } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {error (401, 'not logged in');}if (!locals .user .isAdmin ) {error (403, 'not an admin');}};
呼叫 `error(...)` 會擲回例外,讓您能輕鬆地從輔助函式內部停止執行。
如果擲回 意外 錯誤,SvelteKit 會呼叫 handleError
,並將其視為 500 內部錯誤。
在 SvelteKit 1.x 中,您必須自己 `throw` 錯誤
重新導向永久連結
若要重新導向使用者,請使用 `@sveltejs/kit` 中的 `redirect` 輔助函式,以指定應將其重新導向到的位置,以及 `3xx` 狀態碼。與 `error(...)` 相同,呼叫 `redirect(...)` 會擲回例外,讓您能輕鬆地從輔助函式內部停止執行。
ts
import {redirect } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {redirect (307, '/login');}}
ts
import {redirect } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {redirect (307, '/login');}};
請勿在 `try {...}` 區塊內使用 `redirect()`,因為重新導向會立即觸發 catch 陳述式。
在瀏覽器中,您也可以使用 goto
從 $app.navigation
在 `load` 函式外部以程式方式導覽。
在 SvelteKit 1.x 中,您必須自己 `throw` `redirect`
使用 Promise 串流永久連結
使用伺服器 `load` 時,Promise 會在解析時串流到瀏覽器。如果您有非必要的慢速資料,這會很有用,因為您可以在所有資料都可用之前開始呈現頁面
ts
/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug )};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug ),};};
例如,這對於建立骨架載入狀態很有用
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
串流資料時,請小心正確處理 Promise 拒絕。更具體地說,如果延遲載入的 Promise 在開始呈現之前失敗(此時會被捕獲),而且沒有以某種方式處理錯誤,伺服器可能會因「未處理的 Promise 拒絕」錯誤而崩潰。在 `load` 函式中直接使用 SvelteKit 的 `fetch` 時,SvelteKit 會為您處理這個案例。對於其他 Promise,只要附加一個 noop-catch
到 Promise,將其標記為已處理就夠了。
ts
/** @type {import('./$types').PageServerLoad} */export functionload ({fetch }) {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject ()};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = ({fetch }) => {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject (),};};
在不支援串流的平台(例如 AWS Lambda 或 Firebase)上,回應會被暫存。這表示頁面只會在所有 Promise 都解析後才會呈現。如果您使用代理伺服器(例如 NGINX),請確保它不會暫存來自代理伺服器的回應。
串流資料只會在啟用 JavaScript 時運作。如果您要伺服器呈現頁面,您應避免從通用 `load` 函式傳回 Promise,因為這些 Promise 不會 被串流 — 相反地,當函式在瀏覽器中重新執行時,Promise 會被重新建立。
一旦回應開始串流,就無法變更標頭和狀態碼,因此您無法在串流承諾中設定標頭或擲回重新導向。
在 SvelteKit 1.x 中,頂層承諾會自動等待,只有巢狀承諾會串流。
並行載入永久連結
在呈現(或導覽至)頁面時,SvelteKit 會同時執行所有 `load` 函式,避免請求瀑布效應。在用戶端導覽期間,呼叫多個伺服器 `load` 函式的結果會分組成單一回應。一旦所有 `load` 函式都傳回,頁面就會呈現。
重新執行載入函式永久連結
SvelteKit 會追蹤每個 `load` 函式的相依性,以避免在導覽期間不必要地重新執行它。
例如,給定一對像這樣的 `load` 函式...
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
...在 `+page.server.js` 中的函式會在我們從 ` /blog/trying-the-raw-meat-diet` 導覽到 ` /blog/i-regret-my-choices` 時重新執行,因為 `params.slug` 已變更。在 `+layout.server.js` 中的函式不會重新執行,因為資料仍然有效。換句話說,我們不會第二次呼叫 `db.getPostSummaries()`。
如果父 `load` 函式重新執行,呼叫 `await parent()` 的 `load` 函式也會重新執行。
相依性追蹤不適用於 `load` 函式傳回之後 — 例如,在巢狀 承諾 中存取 `params.x` 函式不會在 `params.x` 變更時導致函式重新執行。(別擔心,如果您不小心這樣做,您會在開發期間收到警告。)相反地,在 `load` 函式的 main body 中存取參數。
搜尋參數會獨立於 URL 的其他部分追蹤。例如,在 `load` 函式中存取 `event.url.searchParams.get("x")` 會在從 `?x=1` 導覽到 `?x=2` 時重新執行該 `load` 函式,但不會在從 `?x=1&y=1` 導覽到 `?x=1&y=2` 時重新執行。
取消追蹤相依性永久連結
在少數情況下,您可能希望從相依性追蹤機制中排除某些內容。您可以使用提供的 `untrack` 函式執行此操作
ts
/** @type {import('./$types').PageLoad} */export async functionload ({untrack ,url }) {// Untrack url.pathname so that path changes don't trigger a rerunif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({untrack ,url }) => {// Untrack url.pathname so that path changes don't trigger a rerunif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}};
手動失效永久連結
您也可以使用 invalidate(url)
重新執行適用於目前頁面的 `load` 函式,這會重新執行所有相依於 `url` 的 `load` 函式,以及 invalidateAll()
,這會重新執行每個 `load` 函式。伺服器載入函式絕不會自動相依於已擷取的 `url`,以避免將機密洩漏給用戶端。
如果 `load` 函式呼叫 `fetch(url)` 或 `depends(url)`,則它相依於 `url`。請注意,`url` 可以是自訂識別碼,開頭為 `[a-z]:`:
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,depends }) {// load reruns when `invalidate('https://api.example.com/random-number')` is called...constresponse = awaitfetch ('https://api.example.com/random-number');// ...or when `invalidate('app:random')` is calleddepends ('app:random');return {number : awaitresponse .json ()};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,depends }) => {// load reruns when `invalidate('https://api.example.com/random-number')` is called...constresponse = awaitfetch ('https://api.example.com/random-number');// ...or when `invalidate('app:random')` is calleddepends ('app:random');return {number : awaitresponse .json (),};};
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageData} */
export let data;
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button on:click={rerunLoadFunction}>Update random number</button>
<script lang="ts">
import { invalidate, invalidateAll } from '$app/navigation';
import type { PageData } from './$types';
export let data: PageData;
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate((url) => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button on:click={rerunLoadFunction}>Update random number</button>
何時會重新執行載入函式?永久連結
總之,load
函式會在以下情況重新執行
- 它參照
params
的屬性,而該屬性的值已變更 - 它參照
url
的屬性(例如url.pathname
或url.search
),而該屬性的值已變更。request.url
中的屬性不會被追蹤 - 它呼叫
url.searchParams.get(...)
、url.searchParams.getAll(...)
或url.searchParams.has(...)
,而問題中的參數發生變更。存取url.searchParams
的其他屬性會產生與存取url.search
相同的效果。 - 它呼叫
await parent()
,而父load
函式重新執行 - 它透過
fetch
(僅限通用載入)或depends
宣告對特定 URL 的相依性,而該 URL 已使用invalidate(url)
標記為無效 - 所有 active
load
函式都已使用invalidateAll()
強制重新執行
params
和 url
可能會因應 <a href="..">
連結點擊、<form>
互動、goto
呼叫或 redirect
而變更。
請注意,重新執行 load
函式會更新對應 +layout.svelte
或 +page.svelte
內部的 data
prop;它不會導致重新建立元件。因此,內部狀態會被保留。如果您不想要這樣的結果,您可以在 afterNavigate
回呼內部重設所有需要重設的內容,或者將元件包在 {#key ...}
區塊中。
對驗證的影響永久連結
載入資料的幾個功能對驗證檢查有重要的影響
- Layout
load
函式並非在每個請求中執行,例如在子路由之間的用戶端導覽期間。 (何時會重新執行載入函式?) - Layout 和 Page
load
函式會同時執行,除非呼叫了await parent()
。如果 Layoutload
擲回例外,Pageload
函式會執行,但用戶端不會收到傳回的資料。
有幾種可能的策略可以確保驗證檢查在受保護程式碼之前發生。
為防止資料瀑布效應並保留版面配置,請快取 load
- 在任何
load
函式執行之前,使用 hooks 來保護多個路由 - 在
+page.server.js
load
函式中直接使用驗證防護,以保護特定路由
在 +layout.server.js
中放置驗證防護,則所有子頁面在執行受保護程式碼之前,都必須呼叫 await parent()
。除非每個子頁面都依賴於 await parent()
傳回的資料,否則其他選項將會執行得更有效率。