首页 / Svelte 5 入门教程 / 路由基础

Svelte 5 入门教程

路由基础

本教程共 50 篇 · 第 44 篇 · 更新于 2026-08-05 · 约 7 分钟阅读

SvelteSvelteKit路由文件路由布局

44. 路由基础

本节目标:掌握 SvelteKit 的文件路由系统,学会创建页面、动态路由、布局组件和 API 端点,了解路由跳转和链接预加载。

文件路由系统

SvelteKit 用文件目录来定义路由。src/routes 里的每个目录对应一个 URL 路径:

  • src/routes//
  • src/routes/about//about
  • src/routes/blog/[slug]//blog/hello-world(动态参数)

每个路由目录里放以 + 开头的文件,SvelteKit 根据文件名识别用途。

+page.svelte:定义页面

+page.svelte 是页面组件文件。最简单的页面:

<!-- src/routes/+page.svelte -->
<h1>欢迎来到我的网站</h1>
<a href="/about">关于我们</a>
<!-- src/routes/about/+page.svelte -->
<h1>关于本站</h1>
<a href="/">返回首页</a>
Note

SvelteKit 用标准的 <a> 标签做导航,不需要专门的 <Link> 组件。点击 <a> 时,SvelteKit 拦截默认跳转,改为客户端路由。

+page.js 和 +page.server.js:加载数据

页面需要数据时,加一个 +page.js 文件,导出 load 函数:

// src/routes/blog/[slug]/+page.js
import { error } from '@sveltejs/kit';

/** @type {import('./$types').PageLoad} */
export function load({ params }) {
	if (params.slug === 'hello-world') {
		return {
			title: 'Hello world!',
			content: '欢迎来到博客...'
		};
	}

	error(404, '文章不存在');
}

页面组件通过 data prop 接收数据:

<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
	/** @type {import('./$types').PageProps} */
	let { data } = $props();
</script>

<h1>{data.title}</h1>
<div>{data.content}</div>

+page.js+page.server.js 的区别:

文件运行环境适用场景
+page.js服务端 + 客户端通用数据加载,可在导航时浏览器端运行
+page.server.js仅服务端访问数据库、使用私密环境变量
Tip

需要访问数据库或 API 密钥时用 +page.server.js。数据可以在浏览器端用公开 API 获取时用 +page.js

动态路由

用方括号创建动态参数:

src/routes/blog/[slug]/+page.svelte   → /blog/hello-world
src/routes/blog/[slug]/+page.svelte   → /blog/svelte-5-guide

slug 参数在 load 函数和页面组件中都能访问:

<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
	/** @type {import('./$types').PageProps} */
	let { data, params } = $props();
</script>

<h1>{data.title}</h1>
<p>当前 slug: {params.slug}</p>

+layout.svelte:共享布局

多个页面共享导航栏、侧边栏等元素时,用布局组件。在 src/routes/+layout.svelte 定义根布局:

<!-- src/routes/+layout.svelte -->
<script>
	let { children } = $props();
</script>

<nav>
	<a href="/">首页</a>
	<a href="/about">关于</a>
	<a href="/settings">设置</a>
</nav>

<main>
	{@render children()}
</main>

{@render children()} 渲染当前页面内容。布局组件在页面切换时不会重新创建,只有 children 部分更新。

布局可以嵌套。/settings 下的子页面可以有自己的布局:

<!-- src/routes/settings/+layout.svelte -->
<script>
	/** @type {import('./$types').LayoutProps} */
	let { data, children } = $props();
</script>

<h1>设置</h1>

<div class="submenu">
	{#each data.sections as section}
		<a href="/settings/{section.slug}">{section.title}</a>
	{/each}
</div>

{@render children()}

+layout.js 和 +layout.server.js

布局也可以有 load 函数,数据会传给所有子页面:

// src/routes/settings/+layout.js
/** @type {import('./$types').LayoutLoad} */
export function load() {
	return {
		sections: [
			{ slug: 'profile', title: '个人资料' },
			{ slug: 'notifications', title: '通知' }
		]
	};
}

子页面能直接访问布局数据:

<!-- src/routes/settings/profile/+page.svelte -->
<script>
	/** @type {import('./$types').PageProps} */
	let { data } = $props();
</script>

<!-- data 包含布局的 load 数据和页面的 load 数据 -->
<p>{data.sections[0].title}</p>

+error.svelte:错误页面

load 函数抛出错误时,SvelteKit 渲染 +error.svelte

<!-- src/routes/blog/[slug]/+error.svelte -->
<script>
	import { page } from '$app/state';
</script>

<h1>{page.status}: {page.error.message}</h1>

$app/state 提供 page 对象,包含 statuserrorurl 等信息。

Note

SvelteKit 2.12 起用 $app/state 替代旧的 $app/stores。如果你用的是更早版本,用 $app/stores 中的 page store。

错误页面会向上查找——如果 /blog/[slug]/ 下没有 +error.svelte,就去 /blog/ 找,再找不到去 / 找。

+server.js:API 路由

+server.js 文件创建 API 端点,导出对应 HTTP 方法的函数:

// src/routes/api/random-number/+server.js
/** @type {import('./$types').RequestHandler} */
export function GET({ url }) {
	const min = Number(url.searchParams.get('min') ?? '0');
	const max = Number(url.searchParams.get('max') ?? '1');
	const random = min + Math.random() * (max - min);

	return new Response(String(random));
}

支持 GETPOSTPATCHPUTDELETE 等方法。用 @sveltejs/kit 的辅助函数简化响应:

import { json, error } from '@sveltejs/kit';

export async function POST({ request }) {
	const { a, b } = await request.json();
	return json(a + b);
}

路由跳转:goto()

除了用 <a> 标签,还可以编程式跳转:

<script>
	import { goto } from '$app/navigation';

	let searchQuery = $state('');

	function handleSearch() {
		goto(`/search?q=${searchQuery}`);
	}
</script>

<input bind:value={searchQuery} />
<button onclick={handleSearch}>搜索</button>

goto 的选项:

goto('/about', {
	replaceState: true,  // 替换历史记录而不是新增
	noScroll: true,      // 不滚动到页面顶部
	keepFocus: true      // 保持当前焦点元素
});
Tip

外部 URL 跳转用 window.location = url,不要用 goto()goto 只处理应用内部路由。

链接预加载

SvelteKit 会在用户 hover 链接时预加载页面代码和数据,让点击后的跳转几乎零延迟。

默认模板在 app.html<body> 上设置了全局预加载:

<body data-sveltekit-preload-data="hover">

两个选项:

行为
hover鼠标悬停时预加载数据和代码(默认)
tap点击时才开始预加载

单个链接可以覆盖全局设置:

<a href="/realtime" data-sveltekit-preload-data="tap">
	实时数据(不预加载)
</a>

还可以只预加载代码不预加载数据:

<a href="/blog/post" data-sveltekit-preload-code="viewport">
	进入视口就预加载代码
</a>
Note

用户开启了省流量模式(navigator.connection.saveData)时,预加载会自动禁用。

其他链接属性

属性作用
data-sveltekit-reload点击时整页刷新,不用客户端路由
data-sveltekit-replacestate替换历史记录而非新增
data-sveltekit-noscroll跳转后不滚动到顶部
<a href="/external-page" data-sveltekit-reload>外部页面</a>
<a href="/tab2" data-sveltekit-replacestate>标签页切换</a>

路由文件速查表

文件作用运行环境
+page.svelte页面组件服务端 + 客户端
+page.js页面数据加载(通用)服务端 + 客户端
+page.server.js页面数据加载(服务端)仅服务端
+layout.svelte布局组件服务端 + 客户端
+layout.js布局数据加载(通用)服务端 + 客户端
+layout.server.js布局数据加载(服务端)仅服务端
+error.svelte错误页面服务端 + 客户端
+server.jsAPI 端点仅服务端

本节回顾

  • SvelteKit 用文件目录定义路由,src/routes/about/ 对应 /about
  • +page.svelte 定义页面,+page.js/+page.server.js 加载数据
  • [slug] 方括号创建动态路由参数
  • +layout.svelte 定义共享布局,用 {@render children()} 渲染页面内容
  • 布局可嵌套,布局的 load 数据会传给所有子页面
  • +error.svelte 处理错误页面,向上查找最近的错误边界
  • +server.js 创建 API 端点,导出 GET/POST 等方法
  • goto() 编程式跳转,data-sveltekit-preload-data 控制预加载行为