高级
钩子
在 GitHub 上编辑此页面“钩子”是您声明的应用范围函数,SvelteKit 将在特定事件响应中调用它,让您能够精细地控制框架的行为。
有三个钩子文件,全部都是可选的
src/hooks.server.js
— 您的应用的服务器钩子src/hooks.client.js
— 您的应用的客户端钩子src/hooks.js
— 您的应用的钩子,在客户端和服务器上运行
这些模块中的代码将在应用程序启动时运行,因此非常适合初始化数据库客户端等。
您可以使用
config.kit.files.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(绕过位于 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 );};
凭据
对于同源请求,SvelteKit 的 fetch
实现将转发 cookie
和 authorization
标头,除非将 credentials
选项设置为 "omit"
。
对于跨源请求,如果请求 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 );};
共享钩子永久链接
以下内容可以添加到 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
中。通用钩子在服务器和客户端上运行(不要与特定于环境的共享钩子混淆)。
reroute永久链接
此函数在 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
的值。