SvelteKit 全栈应用:路由、服务端加载与适配
SvelteKit 全栈应用入门
SvelteKit 是基于 Svelte 的全栈框架,它统一了前端渲染、后端接口和数据获取的写法。无论你最终目标是静态站点、单页应用(SPA)还是带服务端渲染(SSR)的全栈服务,SvelteKit 都能用同一套项目结构和代码完成。本教程从零开始,聚焦三个核心模块:路由、服务端加载与适配器。
项目初始化
确保 Node.js 版本在 16 以上,然后运行:
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
选择默认模板(Skeleton project),开启 TypeScript 是可选的,但推荐使用。
路由系统深入
SvelteKit 使用文件系统驱动的路由,所有页面文件放在 src/routes 下。路由不仅仅是 URL 匹配,还能定义布局、错误边界和服务器端行为。
基础页面与布局
+page.svelte:定义具体页面的 UI 组件。+layout.svelte:定义共享布局,子页面的内容会插入<slot />。
示例:根布局与首页
src/routes/
├ +layout.svelte
├ +page.svelte
└ about/
└ +page.svelte
+layout.svelte 中写入:
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
<slot />
</main>
+page.svelte:
<h1>Welcome to My SvelteKit App</h1>
访问 /about 时,会复用相同的导航栏布局。
动态路由与参数
用方括号包裹参数名来创建动态路由:
src/routes/blog/[slug]/
└ +page.svelte
在 +page.svelte 中可以通过 $page store 拿到参数:
<script>
import { page } from '$app/stores';
</script>
<h1>Post: {$page.params.slug}</h1>
可选参数与其余路径
[[optional]]:可选参数,例如src/routes/product/[[id]]/+page.svelte匹配/product和/product/123。[...rest]:匹配任意深度路径,如src/routes/docs/[...path]/+page.svelte可匹配/docs/a/b/c。
错误页面
每个路由层级都可以有 +error.svelte,用于优雅地展示错误信息。它接收 error 和 status 属性。
<script>
export let error;
export let status;
</script>
<h1>{status} Error</h1>
<p>{error.message}</p>
服务端数据加载
SvelteKit 使用 load 函数从服务端或客户端获取页面所需数据,并将其传递给页面组件。有两种类型的 load 文件:
+page.server.js:仅运行在服务端,可以使用数据库、私密 API 密钥等。+page.js:可能运行在客户端或服务端(取决于场景),适合调用公开 API 或与浏览器相关的操作。
通常优先使用 server load,保证安全与性能。
编写 Server Load 函数
创建 src/routes/blog/+page.server.js:
/** @type {import('./$types').PageServerLoad} */
export async function load() {
// 模拟从数据库获取文章列表
const posts = [
{ slug: 'hello-world', title: 'Hello World' },
{ slug: 'sveltekit-rocks', title: 'SvelteKit Rocks' }
];
return { posts };
}
在对应的 +page.svelte 中自动接收 data 属性:
<script>
export let data;
</script>
<h1>Blog</h1>
<ul>
{#each data.posts as post}
<li><a href="/blog/{post.slug}">{post.title}</a></li>
{/each}
</ul>
动态参数与 Server Load
对于 src/routes/blog/[slug]/+page.server.js:
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
// 根据 params.slug 查询数据库
const post = await getPostBySlug(params.slug);
if (!post) {
throw error(404, 'Post not found');
}
return { post };
}
使用 SvelteKit 提供的 error 辅助函数可以立即返回错误状态页。
Layout Server Load 与数据共享
布局文件也可以有 +layout.server.js,其返回的数据对所有子页面都可见。例如,在根布局加载当前用户信息:
// src/routes/+layout.server.js
export async function load({ locals }) {
return {
user: locals.user
};
}
然后子页面通过 $page.data.user 或在 +page.svelte 中通过 export let data 访问(data 会自动合并父布局的数据)。
流式加载与 Promises
如果需要先渲染页面框架,再将稍后获取的数据流式传输到页面,可以直接返回 Promise,然后在页面中使用 {#await}:
export async function load() {
return {
delayedInfo: fetchSlowData() // 返回 Promise
};
}
{#await data.delayedInfo}
<p>Loading more info...</p>
{:then info}
<p>{info}</p>
{/await}
适配器与部署
适配器(Adapter)负责将 SvelteKit 构建产出转换成特定平台需要的格式。一个 SvelteKit 应用可以部署到 Node.js 服务器、无服务器函数、静态文件托管或边缘网络,只需切换适配器即可。
常用适配器
@sveltejs/adapter-auto:自动检测目标环境(Vercel、Netlify、Cloudflare Pages 等),适用于零配置部署。@sveltejs/adapter-node:构建为 Node.js 服务端应用,可以部署到任何支持 Node 的主机。@sveltejs/adapter-static:将整个应用预渲染为纯静态文件,适合纯内容站点。@sveltejs/adapter-vercel/adapter-netlify:针对特定平台的优化。
安装与配置
以 Node 适配器为例,安装:
npm i -D @sveltejs/adapter-node
修改 svelte.config.js:
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter()
}
};
运行 npm run build 后,输出会在 build/ 目录下。用 Node 启动:
node build/index.js
静态适配器与预渲染
许多网站只需静态文件。安装 adapter-static,并在 svelte.config.js 中配置:
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: undefined
})
}
};
如果需要完全静态,还需要为想要预渲染的路由添加 +page.js 并设置 export const prerender = true;,或使用根布局的 +layout.js 统一设置。动态路由需要提供 entries 方法或在 svelte.config.js 中实现 prerender.entries。
示例:为博客所有文章生成静态页面
在 src/routes/blog/[slug]/+page.js(注意,是 +page.js,可运行在服务端和客户端):
export const prerender = true;
export async function entries() {
// 获取所有有效的 slug
const posts = await fetchPosts();
return posts.map(post => ({ slug: post.slug }));
}
环境变量与运行时
服务端 load 可以通过 process.env 或 $env/static/private 模块访问私密环境变量。SvelteKit 会安全地处理这些变量,不会暴露给客户端。
完整示例:构建一个博客应用
结合所学,我们快速搭建一个博客,包含首页文章列表、文章详情和静态化部署。
1. 路由结构
src/routes/
├ +layout.svelte
├ +layout.server.js (加载站点配置)
├ +page.svelte (首页,文章列表)
├ +page.server.js
└ blog/
└ [slug]/
├ +page.svelte
├ +page.server.js
└ +page.js (用于静态预渲染的 entries 声明)
2. 数据获取模拟
创建 src/lib/posts.js:
const posts = [
{ slug: 'sveltekit-intro', title: 'SvelteKit 入门', content: '...' },
{ slug: 'routing-deep-dive', title: '深入路由', content: '...' }
];
export async function getAllPosts() {
return posts;
}
export async function getPostBySlug(slug) {
return posts.find(p => p.slug === slug) || null;
}
3. 首页 server load
src/routes/+page.server.js:
import { getAllPosts } from '$lib/posts';
export async function load() {
const posts = await getAllPosts();
return { posts };
}
+page.svelte 循环展示列表。
4. 文章详情
src/routes/blog/[slug]/+page.server.js:
import { getPostBySlug } from '$lib/posts';
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const post = await getPostBySlug(params.slug);
if (!post) throw error(404, '文章未找到');
return { post };
}
+page.svelte 展示标题和内容。
5. 预渲染静态版本
src/routes/blog/[slug]/+page.js:
import { getAllPosts } from '$lib/posts';
export const prerender = true;
export async function entries() {
const posts = await getAllPosts();
return posts.map(p => ({ slug: p.slug }));
}
然后使用 adapter-static 构建,即可得到完全静态的博客站点,可以直接部署到 GitHub Pages 或 CDN。
总结
SvelteKit 的文件路由结构直观且强大,load 函数将数据获取与页面紧耦合,同时通过 server / client 分离保障安全。适配器让同一套代码可以输出不同部署形态,从小型静态站到全栈服务都游刃有余。掌握这些核心概念后,你便可以借助 SvelteKit 快速构建高性能、类型安全的现代 Web 应用。