Hooks 与错误处理
本教程共 50 篇 · 第 50 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
本节目标:掌握 SvelteKit 的 Hooks 系统(handle、handleFetch、handleError),学会自定义错误页面(+error.svelte)、重定向和兜底错误处理。
Hooks 是什么
Hooks 是应用级别的函数,SvelteKit 在特定事件发生时调用它们。三个 hooks 文件:
| 文件 | 运行环境 | 用途 |
|---|---|---|
src/hooks.server.js | 仅服务端 | 处理请求、数据库初始化 |
src/hooks.client.js | 仅客户端 | 客户端错误处理 |
NoteSvelteKit 2 没有统一的
hooks.js,服务端和客户端的 hooks 是分开的两个文件。
这些文件在应用启动时加载,适合做初始化工作。
handle:请求拦截
handle 在每次服务端收到请求时运行。你可以修改请求、添加响应头,甚至完全绕过 SvelteKit:
// src/hooks.server.js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
// 拦截特定路径
if (event.url.pathname.startsWith('/custom')) {
return new Response('自定义响应');
}
// 正常处理请求
const response = await resolve(event);
// 添加自定义响应头
response.headers.set('x-custom-header', 'hello');
return response;
}
resolve(event) 渲染路由并生成 Response。你可以在它之前修改请求,之后修改响应。
locals:传递请求数据
在 handle 中给 event.locals 赋值,load 函数和 actions 就能访问:
// src/hooks.server.js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
// 从 cookie 获取用户信息
const sessionid = event.cookies.get('sessionid');
event.locals.user = await getUser(sessionid);
return resolve(event);
}
// src/routes/+layout.server.js
/** @type {import('./$types').LayoutServerLoad} */
export function load(event) {
return {
user: event.locals.user // 来自 handle 的数据
};
}
在 app.d.ts 中声明 Locals 类型:
declare global {
namespace App {
interface Locals {
user: {
name: string;
email: string;
} | null;
}
}
}
export {};
Tip
handle是实现认证的常用位置。在handle中验证 cookie、设置locals.user,后续load和 actions 都能拿到用户信息。
多个 handle 函数
用 sequence 串联多个 handle 函数:
import { sequence } from '@sveltejs/kit/hooks';
/** @type {import('@sveltejs/kit').Handle} */
async function auth({ event, resolve }) {
event.locals.user = await getUser(event.cookies.get('sessionid'));
return resolve(event);
}
/** @type {import('@sveltejs/kit').Handle} */
async function logging({ event, resolve }) {
const response = await resolve(event);
console.log(`${event.request.method} ${event.url.pathname}`);
return response;
}
export const handle = sequence(auth, logging);
handleFetch:拦截 fetch
handleFetch 可以修改 load 函数中 fetch 的行为。比如 SSR 时把外部 API 请求改为内部地址:
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ request, fetch }) {
if (request.url.startsWith('https://api.myapp.com/')) {
// SSR 时直接访问内网地址
request = new Request(
request.url.replace('https://api.myapp.com/', 'http://localhost:9999/'),
request
);
}
return fetch(request);
}
handleError:错误上报
handleError 在未捕获的错误发生时调用。用于日志记录和错误上报:
// src/hooks.server.js
import * as Sentry from '@sentry/sveltekit';
/** @type {import('@sveltejs/kit').HandleServerError} */
export async function handleError({ error, event, status, message }) {
const errorId = crypto.randomUUID();
// 发送到 Sentry
Sentry.captureException(error, {
extra: { event, errorId, status }
});
// 返回给用户的错误对象(会变成 page.error)
return {
message: '服务器出错了',
errorId
};
}
客户端也有 handleError:
// src/hooks.client.js
/** @type {import('@sveltejs/kit').HandleClientError} */
export async function handleError({ error, event, status, message }) {
console.error(error);
return { message: '客户端出错了' };
}
Note
handleError只处理未预期的错误。用error()函数抛出的预期错误不会触发它。
预期错误 vs 未预期错误
SvelteKit 区分两种错误:
预期错误:用 @sveltejs/kit 的 error() 函数主动抛出:
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const post = await db.getPost(params.slug);
if (!post) {
error(404, {
message: '文章不存在',
code: 'NOT_FOUND'
});
}
return { post };
}
error() 会抛出异常,SvelteKit 捕获后设置状态码并渲染 +error.svelte。
未预期错误:代码中的 bug、异常等。消息被隐藏,用户只看到 { message: "Internal Error" }。
+error.svelte:错误页面
每个路由目录可以放 +error.svelte 自定义错误页面:
<!-- src/routes/blog/[slug]/+error.svelte -->
<script>
import { page } from '$app/state';
</script>
<h1>{page.status}</h1>
<p>{page.error.message}</p>
page 对象包含 status(HTTP 状态码)和 error(错误对象)。
错误页面会向上查找——/blog/[slug]/ 下没有就去 /blog/,再没有去 /。
Note
+error.svelte不会处理handle或+server.js中的错误。这些错误返回 JSON 或error.html兜底页面。
redirect:重定向
load 函数或 actions 中用 redirect 跳转:
import { redirect } from '@sveltejs/kit';
export function load() {
redirect(308, '/new-location');
}
常用状态码:
| 状态码 | 含义 |
|---|---|
| 301 | 永久重定向(GET) |
| 302 | 临时重定向(GET) |
| 307 | 临时重定向(保持方法) |
| 308 | 永久重定向(保持方法) |
Tip路由迁移时用 308 永久重定向。表单提交后跳转用 303(POST 后 GET)。
error.html:兜底错误页
当 +error.svelte 也出错,或错误发生在根布局之外,SvelteKit 用 error.html 兜底:
<!-- src/error.html -->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>%sveltekit.error.message%</title>
</head>
<body>
<h1>出错了</h1>
<p>状态码: %sveltekit.status%</p>
<p>消息: %sveltekit.error.message%</p>
</body>
</html>
这个文件是纯 HTML,没有 Svelte 组件,用于最严重的错误场景。
自定义错误类型
用 App.Error 接口扩展错误对象的类型:
// src/app.d.ts
declare global {
namespace App {
interface Error {
message: string;
code: string;
errorId: string;
}
}
}
export {};
这样 page.error 就有类型提示,+error.svelte 中可以安全地访问 page.error.code。
Hooks 速查表
| Hook | 文件 | 触发时机 | 用途 |
|---|---|---|---|
handle | hooks.server.js | 每次请求 | 认证、修改请求/响应 |
handleFetch | hooks.server.js | load 中调用 fetch | 修改 fetch 目标 |
handleError | hooks.server.js + hooks.client.js | 未捕获错误 | 日志、错误上报 |
handleValidationError | hooks.server.js | 远程函数参数校验失败 | 自定义校验错误 |
本节回顾
handle拦截每个请求,可以设置event.locals传递数据,用sequence串联多个handleFetch修改load中的fetch请求,SSR 时可改为内网地址handleError处理未预期错误,用于日志和错误上报(Sentry 等)- 预期错误用
error()抛出,未预期错误由handleError捕获 +error.svelte自定义错误页面,向上查找最近的错误边界redirect()做重定向,error.html是最后的兜底页面App.Error接口扩展错误对象类型,App.Locals声明请求局部数据类型