進階
Hooks
在 GitHub 上編輯此頁面「Hooks」是您宣告的應用程式範圍函式,SvelteKit 會在特定事件發生時呼叫,讓您能精細地控制架構的行為。
有三個 hooks 檔案,全部都是選用的
src/hooks.server.js
— 您的應用程式伺服器 hookssrc/hooks.client.js
— 您的應用程式用戶端 hookssrc/hooks.js
— 您的應用程式 hooks,會在用戶端和伺服器上執行
這些模組中的程式碼會在應用程式啟動時執行,因此可以用來初始化資料庫用戶端等等。
您可以使用
config.kit.files.hooks
來設定這些檔案的位置。
伺服器 hooks永久連結
下列 hooks 可以新增到 src/hooks.server.js
handle永久連結
這個函式會在 SvelteKit 伺服器每次收到 請求 時執行,無論是在應用程式執行期間,或是在 預先渲染 期間,並決定 回應。它會收到一個表示請求的 event
物件,以及一個稱為 resolve
的函式,用來渲染路由並產生一個 Response
。這讓您可以修改回應標頭或本文,或完全繞過 SvelteKit(例如,用來以程式化方式實作路由)。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {if (event .url .pathname .startsWith ('/custom')) {return newResponse ('custom response');}constresponse = awaitresolve (event );returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {if (event .url .pathname .startsWith ('/custom')) {return newResponse ('custom response');}constresponse = awaitresolve (event );returnresponse ;};
SvelteKit 不會 處理靜態資源的請求,其中包括已經預先渲染的頁面。
如果未實作,則預設為 ({ event, resolve }) => resolve(event)
。若要將自訂資料新增到請求(會傳遞給 +server.js
和伺服器 load
函式中的處理常式),請填入 event.locals
物件,如下所示。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {event .locals .user = awaitgetUserInformation (event .cookies .get ('sessionid'));constresponse = awaitresolve (event );response .headers .set ('x-custom-header', 'potato');returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {event .locals .user = awaitgetUserInformation (event .cookies .get ('sessionid'));constresponse = awaitresolve (event );response .headers .set ('x-custom-header', 'potato');returnresponse ;};
您可以定義多個 handle
函式,並使用 sequence
輔助函式 來執行它們。
resolve
也支援第二個選用參數,讓您可以更進一步地控制回應的渲染方式。該參數是一個物件,可以包含下列欄位
transformPageChunk(opts: { html: string, done: boolean }): MaybePromise<string | undefined>
— 對 HTML 套用自訂轉換。如果done
為 true,則為最後一個區塊。區塊不保證是格式良好的 HTML(例如,它們可能包含元素的開啟標籤,但不包含其關閉標籤),但它們一定會在合理的界線上分割,例如%sveltekit.head%
或版面/頁面元件。filterSerializedResponseHeaders(name: string, value: string): boolean
— 決定當load
函式使用fetch
載入資源時,哪些標頭應包含在序列化回應中。預設情況下,不會包含任何標頭。preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean
— 決定應將哪些檔案新增至<head>
標籤以預先載入。此方法會在建置時間找到的每個檔案中呼叫,同時建構程式碼區塊 — 因此,如果你在+page.svelte
中有import './styles.css
,則在造訪該頁面時,preload
會使用解析後的路徑呼叫該 CSS 檔案。請注意,在開發模式中,preload
不會 被呼叫,因為它依賴於建置時間發生的分析。預先載入可以透過提早下載資源來改善效能,但如果過度下載不必要的資源,也可能會造成損害。預設情況下,js
和css
檔案將會預先載入。目前並未預先載入asset
檔案,但我們可能會在評估回饋後稍後新增此功能。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {constresponse = awaitresolve (event , {transformPageChunk : ({html }) =>html .replace ('old', 'new'),filterSerializedResponseHeaders : (name ) =>name .startsWith ('x-'),preload : ({type ,path }) =>type === 'js' ||path .includes ('/important/')});returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {constresponse = awaitresolve (event , {transformPageChunk : ({html }) =>html .replace ('old', 'new'),filterSerializedResponseHeaders : (name ) =>name .startsWith ('x-'),preload : ({type ,path }) =>type === 'js' ||path .includes ('/important/'),});returnresponse ;};
請注意,resolve(...)
永遠不會擲回錯誤,它會永遠傳回具有適當狀態碼的 Promise<Response>
。如果在 handle
期間其他地方擲回錯誤,則會將其視為致命錯誤,而 SvelteKit 會回應錯誤的 JSON 表示形式或後備錯誤頁面 — 可以透過 src/error.html
自訂 — 視 Accept
標頭而定。你可以 在此 閱讀更多關於錯誤處理的資訊。
handleFetch永久連結
此函式允許你修改 (或取代) 在伺服器上 (或在預先渲染期間) 執行 load
或 action
函式中發生的 fetch
要求。
例如,當使用者對應頁面執行用戶端導覽時,你的 load
函式可能會對公共 URL (例如 https://api.yourapp.com
) 發出要求,但在 SSR 期間,直接存取 API 可能比較合理 (繞過它和公共網路之間的任何代理伺服器和負載平衡器)。
ts
/** @type {import('@sveltejs/kit').HandleFetch} */export async functionhandleFetch ({request ,fetch }) {if (request .url .startsWith ('https://api.yourapp.com/')) {// clone the original request, but change the URLrequest = newRequest (request .url .replace ('https://api.yourapp.com/', 'https://127.0.0.1:9999/'),request );}returnfetch (request );}
ts
import type {HandleFetch } from '@sveltejs/kit';export consthandleFetch :HandleFetch = async ({request ,fetch }) => {if (request .url .startsWith ('https://api.yourapp.com/')) {// clone the original request, but change the URLrequest = newRequest (request .url .replace ('https://api.yourapp.com/', 'https://127.0.0.1:9999/'),request ,);}returnfetch (request );};
憑證
對於同源要求,除非將 credentials
選項設定為 "omit"
,否則 SvelteKit 的 fetch
實作會轉發 cookie
和 authorization
標頭。
對於跨來源請求,如果請求 URL 屬於應用程式的子網域,則會包含 cookie
— 例如,如果您的應用程式位於 my-domain.com
,而您的 API 位於 api.my-domain.com
,則 cookie 會包含在請求中。
如果您的應用程式和 API 位於同層子網域 — 例如 www.my-domain.com
和 api.my-domain.com
— 則屬於共同父網域(例如 my-domain.com
)的 cookie 不會 包含在內,因為 SvelteKit 無法得知 cookie 屬於哪個網域。在這些情況下,您需要使用 handleFetch
手動包含 cookie
ts
/** @type {import('@sveltejs/kit').HandleFetch} */export async functionhandleFetch ({event ,request ,fetch }) {if (request .url .startsWith ('https://api.my-domain.com/')) {Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.2345Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.request .headers .set ('cookie',event .request .headers .get ('cookie'));}returnfetch (request );}
ts
import type {HandleFetch } from '@sveltejs/kit';export consthandleFetch :HandleFetch = async ({event ,request ,fetch }) => {if (request .url .startsWith ('https://api.my-domain.com/')) {Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.2345Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.request .headers .set ('cookie',event .request .headers .get ('cookie'));}returnfetch (request );};
共用 hook永久連結
以下內容可以新增到 src/hooks.server.js
和 src/hooks.client.js
handleError永久連結
如果在載入或渲染期間發生 意外錯誤,則會呼叫此函式,並傳入 error
、event
、status
程式碼和 message
。這允許執行兩件事
- 您可以記錄錯誤
- 您可以產生錯誤的客製化表示形式,以安全的方式顯示給使用者,並省略敏感的詳細資料,例如訊息和堆疊追蹤。回傳值預設為
{ message }
,會變成$page.error
的值。
對於從您的程式碼(或您的程式碼呼叫的函式庫程式碼)引發的錯誤,狀態會是 500,而訊息會是「內部錯誤」。雖然 error.message
可能包含不應揭露給使用者的敏感資訊,但 message
是安全的(儘管對一般使用者來說沒有意義)。
若要以類型安全的方式將更多資訊新增到 $page.error
物件中,您可以透過宣告 App.Error
介面(必須包含 message: string
,以確保合理的後備行為)來自訂預期的形狀。這允許您 — 例如 — 附加追蹤 ID,讓使用者在與您的技術支援人員通信時引用
ts
declareglobal {namespaceApp {interfaceError {message : string;errorId : string;}}}export {};
ts
import * asSentry from '@sentry/sveltekit';Sentry .init ({/*...*/})/** @type {import('@sveltejs/kit').HandleServerError} */export async functionhandleError ({error ,event ,status ,message }) {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.2353Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.Sentry .captureException (error , {extra : {event ,errorId ,status }});return {message : 'Whoops!',errorId };}
ts
import * asSentry from '@sentry/sveltekit';import type {HandleServerError } from '@sveltejs/kit';Sentry .init ({/*...*/});export consthandleError :HandleServerError = async ({error ,event ,status ,message }) => {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Sentry .captureException (error , {extra : {event ,errorId ,status },});return {message : 'Whoops!',errorId ,};};
ts
import * asSentry from '@sentry/sveltekit';Sentry .init ({/*...*/})/** @type {import('@sveltejs/kit').HandleClientError} */export async functionhandleError ({error ,event ,status ,message }) {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.2353Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.Sentry .captureException (error , {extra : {event ,errorId ,status }});return {message : 'Whoops!',errorId };}
ts
import * asSentry from '@sentry/sveltekit';import type {HandleClientError } from '@sveltejs/kit';Sentry .init ({/*...*/});export consthandleError :HandleClientError = async ({error ,event ,status ,message }) => {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Sentry .captureException (error , {extra : {event ,errorId ,status },});return {message : 'Whoops!',errorId ,};};
在
src/hooks.client.js
中,handleError
的類型是HandleClientError
,而不是HandleServerError
,而event
是NavigationEvent
,而不是RequestEvent
。
此函式不會針對預期錯誤(使用從 @sveltejs/kit
匯入的 error
函式引發的錯誤)呼叫。
在開發期間,如果因為 Svelte 程式碼中的語法錯誤而發生錯誤,則傳入的錯誤會附加上一個 frame
屬性,以突顯錯誤的位置。
請確定
handleError
絕不會 引發錯誤
通用掛勾永久連結
以下內容可以新增到 src/hooks.js
。通用掛勾可以在伺服器和用戶端上執行(不要與環境特定的共用掛勾混淆)。
重新導向永久連結
此函式在 handle
之前執行,允許您變更 URL 轉換為路由的方式。傳回的路徑名稱(預設為 url.pathname
)用於選擇路由及其參數。
例如,您可能有一個 src/routes/[[lang]]/about/+page.svelte
頁面,可以透過 /en/about
或 /de/ueber-uns
或 /fr/a-propos
存取。您可以使用 reroute
實作此功能
ts
/** @type {Record<string, string>} */consttranslated = {'/en/about': '/en/about','/de/ueber-uns': '/de/about','/fr/a-propos': '/fr/about',};/** @type {import('@sveltejs/kit').Reroute} */export functionreroute ({url }) {if (url .pathname intranslated ) {returntranslated [url .pathname ];}}
ts
import type {Reroute } from '@sveltejs/kit';consttranslated :Record <string, string> = {'/en/about': '/en/about','/de/ueber-uns': '/de/about','/fr/a-propos': '/fr/about',};export constreroute :Reroute = ({url }) => {if (url .pathname intranslated ) {returntranslated [url .pathname ];}};
lang
參數會從傳回的路徑名稱正確衍生而來。
使用 reroute
不會 變更瀏覽器網址列的內容,或 event.url
的值。