SvelteKit 的文件路由系统不仅支持静态路径,还提供了动态参数、布局嵌套和服务端数据加载能力。本章将逐一展开。
动态路由参数
当路由路径不确定时,使用方括号 [slug] 声明动态参数。
src/routes/
└── blog/
├── [slug]/
│ └── +page.svelte → /blog/:slug
└── +page.svelte → /blog
在页面组件中通过 $page.params 获取参数:
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import { page } from '$app/stores';
let slug = $derived($page.params.slug);
</script>
<h1>文章: {slug}</h1>
多段参数
使用 [...path] 语法匹配多级路径:
src/routes/
└── docs/
└── [...path]/
└── +page.svelte → /docs/a/b/c
<script lang="ts">
import { page } from '$app/stores';
let path = $derived($page.params.path);
</script>
<p>文档路径: {path}</p>
<!-- 访问 /docs/guide/getting-started → "guide/getting-started" -->
可选参数
使用 [[param]] 双括号语法让参数可选:
src/routes/
└── [[lang]]/
└── +page.svelte → / 或 /en 或 /zh-CN
布局组件 (+layout.svelte)
布局组件包裹其目录下的所有页面。你可以在布局中定义公共的页头、页脚和侧边栏。
<!-- src/routes/blog/+layout.svelte -->
<script lang="ts">
let { children } = $props();
</script>
<nav>
<a href="/blog">博客首页</a>
<a href="/blog/archive">归档</a>
</nav>
<main>
{@render children()}
</main>
<footer>© 2026 FisherHub</footer>
布局可以嵌套。根级 src/routes/+layout.svelte 包裹整个应用,子目录的布局只作用于对应路由子树。
布局数据 +layout.ts
布局可以拥有自己的 load 函数,共享数据给所有子页面:
// src/routes/blog/+layout.ts
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
return {
categories: ['技术', '生活', '开源'],
siteName: 'FisherHub 博客'
};
};
在布局或子页面中通过 data 属性接收:
<script lang="ts">
let { data, children } = $props();
</script>
<h1>{data.siteName}</h1>
数据加载 (+page.ts load)
+page.ts 的 load 函数在服务端执行(SSR 时),返回的数据会注入到页面的 data 属性。
// src/routes/blog/[slug]/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, fetch }) => {
const slug = params.slug;
// 注意:这里的 fetch 是 SvelteKit 的增强版 fetch
// 在服务端运行时,它会直接调用内部 API,避免额外的 HTTP 请求
const res = await fetch(`/api/posts/${slug}.json`);
if (!res.ok) {
throw error(404, '文章不存在');
}
const post = await res.json();
return {
post
};
};
通用 load 函数
+page.ts 中的 load 既在服务端运行(首次访问),也在客户端运行(后续导航)。如果你只需要在服务端获取数据,使用 +page.server.ts:
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
// 这里可以直接访问数据库、API key 等
const post = await getPostFromDatabase(params.slug);
if (!post) {
throw error(404, '文章不存在');
}
return { post };
};
关键区别
| 文件 | 运行位置 | 使用场景 |
|---|---|---|
+page.ts | 服务端 + 客户端 | 调用公共 API、通用数据获取 |
+page.server.ts | 仅服务端 | 访问数据库、私密凭证、敏感操作 |
错误页面 (+error.svelte)
当 load 函数或页面渲染抛出错误时,SvelteKit 会渲染同级的 +error.svelte。
<!-- src/routes/+error.svelte -->
<script lang="ts">
import { page } from '$app/stores';
import { error } from '@sveltejs/kit';
</script>
<h1>{$page.status}</h1>
<p>{$page.error?.message}</p>
<a href="/">返回首页</a>
你可以为不同的路由层级添加专属错误页面:
src/routes/
├── +error.svelte ← 全局 404/500
└── blog/
├── +error.svelte ← /blog 下的专属错误页
└── [slug]/
└── +error.svelte ← 单篇文章的错误页
数据预取
在链接上使用 data-sveltekit-preload-data 或 data-sveltekit-preload-code 提前加载数据:
<a href="/blog/my-post" data-sveltekit-preload-data>预取数据</a>
也可以在根布局中全局开启:
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { configure } from '$app/stores';
</script>
<div data-sveltekit-preload-data="hover">
{@render children()}
</div>
设置为 hover 后,用户鼠标悬停在链接上时就开始加载数据,导航几乎瞬间完成。
总结
本章介绍了:
- 动态路由参数
[slug]、[...path]、[[param]]的用法 +layout.svelte实现布局嵌套和数据共享+page.ts和+page.server.ts的数据加载机制+error.svelte创建友好的错误页面- 通过
data-sveltekit-preload-data实现预取优化
下一章将学习表单处理和服务端操作。