SvelteKit页面路由深入解析
1. SvelteKit 页面路由基础
SvelteKit 是 Svelte 的官方框架,它提供了一种简洁且强大的方式来构建 web 应用。其中,页面路由是其核心功能之一。在 SvelteKit 中,路由基于文件系统。
1.1 基本路由结构
SvelteKit 项目的 src/routes
目录定义了应用的路由。例如,在 src/routes
目录下创建一个 about.svelte
文件,就会生成一个 /about
路由。这个文件的内容就是对应路由页面的 Svelte 组件代码。
<!-- src/routes/about.svelte -->
<script>
// 组件逻辑
</script>
<main>
<h1>关于我们</h1>
<p>这里是关于我们的详细内容。</p>
</main>
当用户访问 http://your - app - url/about
时,SvelteKit 会渲染 about.svelte
组件。
1.2 嵌套路由
SvelteKit 支持嵌套路由。假设我们有一个博客应用,文章详情页面可能有相关评论等嵌套内容。我们可以在 src/routes/blog
目录下创建子目录和文件来定义嵌套路由。
例如,创建 src/routes/blog/[slug].svelte
用于文章详情页面,在该目录下再创建 comments.svelte
用于文章的评论部分。
<!-- src/routes/blog/[slug].svelte -->
<script>
import Comments from './comments.svelte';
// 获取文章 slug 相关逻辑
const { slug } = $page.params;
</script>
<main>
<h1>{slug} 的文章内容</h1>
<Comments />
</main>
<!-- src/routes/blog/comments.svelte -->
<script>
// 评论相关逻辑
</script>
<main>
<h2>评论</h2>
<ul>
<li>第一条评论</li>
<li>第二条评论</li>
</ul>
</main>
2. 动态路由
动态路由在实际应用中非常常见,比如博客文章根据不同的标题生成不同的 URL,电商商品根据不同的 ID 展示不同的详情页。
2.1 参数化路由
在 SvelteKit 中,通过在文件名中使用方括号 []
来定义参数化路由。例如,[slug].svelte
表示一个动态路由,其中 slug
是参数。
<!-- src/routes/blog/[slug].svelte -->
<script>
import { page } from '$app/stores';
const { slug } = $page.params;
</script>
<main>
<h1>{slug} 的博客文章</h1>
<!-- 根据 slug 获取并展示文章内容的逻辑 -->
</main>
在上述代码中,$page.params
存储了路由参数。当用户访问 http://your - app - url/blog/hello - world
时,slug
的值就是 hello - world
。
2.2 多个参数
我们也可以在一个路由中定义多个参数。例如,[category]/[slug].svelte
可以表示不同分类下的博客文章。
<!-- src/routes/blog/[category]/[slug].svelte -->
<script>
import { page } from '$app/stores';
const { category, slug } = $page.params;
</script>
<main>
<h1>{category} 分类下的 {slug} 文章</h1>
<!-- 根据 category 和 slug 获取并展示文章内容的逻辑 -->
</main>
3. 布局路由
布局路由允许我们为一组页面定义共同的结构,比如导航栏、侧边栏等。
3.1 创建布局
在 src/routes
目录下创建一个 _layout.svelte
文件,这个文件就是整个应用的布局。
<!-- src/routes/_layout.svelte -->
<script>
// 布局相关逻辑,例如设置页面标题等
</script>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
<slot />
在上述代码中,<slot />
是子页面内容的插入点。所有在 src/routes
目录下的页面(如 index.svelte
、about.svelte
等)都会渲染在 <slot />
的位置。
3.2 嵌套布局
除了全局布局,我们还可以在子目录中创建 _layout.svelte
文件来定义嵌套布局。例如,在 src/routes/blog
目录下创建 _layout.svelte
,可以为博客相关页面定义特定的布局。
<!-- src/routes/blog/_layout.svelte -->
<script>
// 博客布局相关逻辑
</script>
<aside>
<h3>博客分类</h3>
<ul>
<li>技术</li>
<li>生活</li>
</ul>
</aside>
<slot />
在这个布局中,<slot />
会插入 src/routes/blog
目录下的页面内容,如 [slug].svelte
等。
4. 路由加载数据
在实际应用中,页面通常需要从服务器获取数据来展示。SvelteKit 提供了便捷的方式来加载数据。
4.1 load 函数
在 SvelteKit 页面组件中,可以定义一个 load
函数来获取数据。这个函数在服务器端运行,在页面渲染之前执行。
<!-- src/routes/blog/[slug].svelte -->
<script context="module">
export async function load({ params }) {
const response = await fetch(`https://api.example.com/blog/${params.slug}`);
const post = await response.json();
return {
post
};
}
</script>
<script>
export let data;
const { post } = data;
</script>
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
在上述代码中,load
函数接收一个参数对象,其中 params
包含路由参数。通过 fetch
获取数据后,将数据返回。在组件内部,通过 export let data
接收 load
函数返回的数据。
4.2 共享数据
如果多个页面需要相同的数据,我们可以在布局组件的 load
函数中获取数据,然后通过 $page.data
共享给子页面。
<!-- src/routes/blog/_layout.svelte -->
<script context="module">
export async function load() {
const response = await fetch('https://api.example.com/blog/categories');
const categories = await response.json();
return {
categories
};
}
</script>
<script>
export let data;
const { categories } = data;
</script>
<aside>
<h3>博客分类</h3>
<ul>
{#each categories as category}
<li>{category.name}</li>
{/each}
</ul>
</aside>
<slot />
<!-- src/routes/blog/[slug].svelte -->
<script>
import { page } from '$app/stores';
const { categories } = $page.data;
</script>
<main>
<!-- 可以使用 categories 数据,例如展示当前文章所属分类等 -->
</main>
5. 路由过渡效果
路由过渡效果可以提升用户体验,让页面切换更加流畅和美观。
5.1 使用 Svelte 过渡
Svelte 本身提供了丰富的过渡效果,我们可以在路由切换时应用这些效果。
首先,在 src/routes/_layout.svelte
中添加过渡效果。
<!-- src/routes/_layout.svelte -->
<script>
import { fade } from 'svelte/transition';
</script>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
<svelte:component this={$page.component} transition:fade />
在上述代码中,通过 svelte:component
标签动态渲染当前页面组件,并应用了 fade
过渡效果。当路由切换时,页面会有淡入淡出的效果。
5.2 自定义过渡
我们也可以自定义过渡效果。例如,创建一个从底部滑入的过渡效果。
<!-- src/routes/_layout.svelte -->
<script>
function slideUp(node, duration = 400) {
const style = getComputedStyle(node);
const opacity = +style.opacity;
const height = +style.height.slice(0, -2);
return {
duration,
css: (t) => `
opacity: ${opacity * (1 - t)};
height: ${height * t}px;
`
};
}
</script>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
<svelte:component this={$page.component} transition:slideUp />
在上述代码中,slideUp
函数定义了从底部滑入的过渡逻辑,t
表示过渡的进度,通过 css
返回不同进度下的样式。
6. 错误处理与 404 页面
在应用中,难免会遇到错误情况,比如数据获取失败或者用户访问不存在的页面。SvelteKit 提供了处理这些情况的机制。
6.1 处理 load 函数中的错误
在 load
函数中,如果发生错误,可以抛出错误。SvelteKit 会捕获这些错误并进行相应处理。
<!-- src/routes/blog/[slug].svelte -->
<script context="module">
export async function load({ params }) {
try {
const response = await fetch(`https://api.example.com/blog/${params.slug}`);
if (!response.ok) {
throw new Error('文章未找到');
}
const post = await response.json();
return {
post
};
} catch (error) {
throw error;
}
}
</script>
<script>
export let data;
export let error;
if (error) {
// 处理错误,例如显示错误信息
} else {
const { post } = data;
}
</script>
<main>
{#if error}
<p>{error.message}</p>
{:else}
<h1>{post.title}</h1>
<p>{post.content}</p>
{/if}
</main>
6.2 404 页面
要创建 404 页面,在 src/routes
目录下创建一个 __error.svelte
文件。
<!-- src/routes/__error.svelte -->
<script>
export let error;
</script>
<main>
<h1>404 - 页面未找到</h1>
<p>{error.message}</p>
</main>
当用户访问不存在的路由时,SvelteKit 会渲染 __error.svelte
组件,并将错误信息传递给它。
7. 路由与导航
在 SvelteKit 应用中,实现页面导航是一个常见需求。
7.1 使用 标签
最基本的导航方式是使用 HTML 的 <a>
标签。例如:
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
这种方式会触发浏览器的完整页面刷新。
7.2 使用 SvelteKit 的导航函数
SvelteKit 提供了 goto
函数来实现无刷新导航。首先,需要导入 goto
函数。
<script>
import { goto } from '$app/navigation';
function navigateToAbout() {
goto('/about');
}
</script>
<button on:click={navigateToAbout}>前往关于页面</button>
在上述代码中,goto
函数会在不刷新整个页面的情况下,切换到指定的路由。这在单页应用中可以提升用户体验,避免不必要的加载。
8. 服务器端渲染与静态站点生成
SvelteKit 支持服务器端渲染(SSR)和静态站点生成(SSG),这对于提高应用的性能和 SEO 非常有帮助。
8.1 服务器端渲染
在 SvelteKit 中,默认情况下,load
函数在服务器端运行,这就是服务器端渲染的基础。服务器会在接收到请求时,运行 load
函数获取数据,然后将带有数据的页面 HTML 发送给客户端。
例如,在 src/routes/blog/[slug].svelte
的 load
函数中获取文章数据,服务器会将文章内容填充到 HTML 中再发送给用户。
<!-- src/routes/blog/[slug].svelte -->
<script context="module">
export async function load({ params }) {
const response = await fetch(`https://api.example.com/blog/${params.slug}`);
const post = await response.json();
return {
post
};
}
</script>
<script>
export let data;
const { post } = data;
</script>
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
8.2 静态站点生成
静态站点生成可以将页面预先生成 HTML 文件,适合内容更新不频繁的应用。在 SvelteKit 中,可以通过在 package.json
中配置 svelte - kit build
命令来生成静态站点。
首先,确保 package.json
中有如下脚本:
{
"scripts": {
"build": "svelte - kit build",
"preview": "svelte - kit preview"
}
}
然后运行 npm run build
,SvelteKit 会遍历所有路由,运行 load
函数获取数据,并生成静态 HTML 文件。这些文件可以部署到任何静态文件服务器上。
例如,对于博客应用,会为每篇文章生成一个静态 HTML 文件,提升页面加载速度和 SEO 效果。
9. 路由的高级应用
在复杂的应用场景中,我们可能需要更高级的路由功能。
9.1 动态导入路由组件
有时候,我们希望根据不同的条件动态导入路由组件。SvelteKit 支持动态导入。
<script>
import { page } from '$app/stores';
let Component;
$: if ($page.url.pathname.startsWith('/admin')) {
import('./admin/AdminDashboard.svelte').then((module) => {
Component = module.default;
});
} else {
import('./public/Home.svelte').then((module) => {
Component = module.default;
});
}
</script>
{#if Component}
<svelte:component this={Component} />
{/if}
在上述代码中,根据当前页面的路径,动态导入不同的组件。如果路径以 /admin
开头,导入管理员仪表盘组件;否则,导入公共首页组件。
9.2 路由中间件
虽然 SvelteKit 没有像一些后端框架那样直接的中间件概念,但我们可以通过自定义逻辑来实现类似功能。例如,在 load
函数中进行身份验证。
<!-- src/routes/dashboard.svelte -->
<script context="module">
export async function load({ locals }) {
const isAuthenticated = locals.user && locals.user.isAuthenticated;
if (!isAuthenticated) {
throw {
status: 401,
message: '请先登录'
};
}
// 加载仪表盘数据的逻辑
const response = await fetch('https://api.example.com/dashboard/data');
const data = await response.json();
return {
data
};
}
</script>
<script>
export let data;
export let error;
if (error) {
// 处理错误,例如跳转到登录页面
} else {
// 展示仪表盘数据
}
</script>
<main>
{#if error}
<p>{error.message}</p>
{:else}
<h1>仪表盘</h1>
<!-- 展示数据的逻辑 -->
{/if}
</main>
在上述代码中,通过检查 locals.user
是否已认证来决定是否允许访问仪表盘页面。如果未认证,抛出错误并在组件中处理。
10. 路由与国际化
在全球化的应用中,路由需要支持国际化。
10.1 基于 URL 的语言切换
一种常见的方式是在 URL 中包含语言参数。例如,/en/about
表示英文的关于页面,/zh/about
表示中文的关于页面。
在 src/routes
目录下,可以创建不同语言前缀的目录,如 src/routes/en
和 src/routes/zh
,然后在这些目录下创建相应语言的页面文件。
<!-- src/routes/en/about.svelte -->
<script>
// 英文页面逻辑
</script>
<main>
<h1>About Us</h1>
<p>Details about our company in English.</p>
</main>
<!-- src/routes/zh/about.svelte -->
<script>
// 中文页面逻辑
</script>
<main>
<h1>关于我们</h1>
<p>关于我们公司的中文详细内容。</p>
</main>
10.2 语言切换逻辑
为了实现语言切换,我们可以在布局组件中添加语言切换按钮,并通过 goto
函数切换到不同语言的路由。
<!-- src/routes/_layout.svelte -->
<script>
import { goto } from '$app/navigation';
function switchToEnglish() {
goto('/en' + $page.url.pathname.slice(3));
}
function switchToChinese() {
goto('/zh' + $page.url.pathname.slice(3));
}
</script>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
<button on:click={switchToEnglish}>英文</button>
<button on:click={switchToChinese}>中文</button>
<svelte:component this={$page.component} />
在上述代码中,switchToEnglish
和 switchToChinese
函数根据当前页面路径,构建不同语言的 URL 并使用 goto
函数导航。
通过以上对 SvelteKit 页面路由的深入解析,我们涵盖了从基础路由结构到高级应用以及国际化等多个方面的内容,希望能帮助开发者在构建 SvelteKit 应用时更好地利用路由功能,打造出优秀的 web 应用。