服务端渲染 #
一、渲染模式概述 #
SvelteKit 支持多种渲染模式,可以根据页面需求灵活配置。
text
┌─────────────────────────────────────────────────────────────┐
│ 渲染模式对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ SSR (服务端渲染) │
│ ├── 首次请求时在服务端生成 HTML │
│ ├── 适合:内容页面、SEO 重要页面 │
│ └── 特点:首屏快、SEO 友好 │
│ │
│ SSG (静态站点生成) │
│ ├── 构建时生成静态 HTML 文件 │
│ ├── 适合:博客、文档、营销页面 │
│ └── 特点:性能最佳、可部署到 CDN │
│ │
│ CSR (客户端渲染) │
│ ├── 浏览器中渲染页面 │
│ ├── 适合:登录后页面、交互密集页面 │
│ └── 特点:服务器压力小、交互流畅 │
│ │
└─────────────────────────────────────────────────────────────┘
二、SSR 配置 #
2.1 启用 SSR #
src/routes/+page.ts:
typescript
export const ssr = true;
export const csr = true;
2.2 禁用 SSR #
src/routes/+page.ts:
typescript
export const ssr = false;
export const csr = true;
2.3 页面级配置 #
src/routes/dashboard/+page.ts:
typescript
export const ssr = false;
export async function load() {
return {
title: 'Dashboard'
};
}
2.4 布局级配置 #
src/routes/(app)/+layout.ts:
typescript
export const ssr = true;
export const csr = true;
三、SSG 静态生成 #
3.1 预渲染页面 #
src/routes/about/+page.ts:
typescript
export const prerender = true;
export async function load() {
return {
title: 'About Us',
content: 'This is a static page.'
};
}
3.2 预渲染配置 #
src/routes/+layout.ts:
typescript
export const prerender = 'auto';
可选值:
true- 预渲染此页面false- 不预渲染'auto'- 自动检测
3.3 动态路由预渲染 #
src/routes/blog/[slug]/+page.ts:
typescript
export const prerender = true;
export async function entries() {
const posts = await getAllPosts();
return posts.map(post => ({
slug: post.slug
}));
}
export async function load({ params }) {
const post = await getPost(params.slug);
return { post };
}
3.4 回退页面 #
src/routes/+layout.ts:
typescript
export const prerender = true;
export const ssr = false;
src/routes/+page.svelte:
svelte
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.url.pathname}</h1>
四、CSR 客户端渲染 #
4.1 纯 CSR 页面 #
src/routes/app/+page.ts:
typescript
export const ssr = false;
export const csr = true;
export const prerender = false;
4.2 交互密集页面 #
src/routes/editor/+page.svelte:
svelte
<script>
import { browser } from '$app/environment';
let editor;
$: if (browser && editor) {
initEditor(editor);
}
</script>
{#if browser}
<div bind:this={editor} class="editor"></div>
{:else}
<p>Loading editor...</p>
{/if}
4.3 动态导入 #
src/routes/+page.svelte:
svelte
<script>
import { onMount } from 'svelte';
let HeavyComponent;
onMount(async () => {
const module = await import('./HeavyComponent.svelte');
HeavyComponent = module.default;
});
</script>
{#if HeavyComponent}
<svelte:component this={HeavyComponent} />
{/if}
五、混合渲染 #
5.1 静态外壳 + 动态内容 #
src/routes/+page.ts:
typescript
export const ssr = true;
export const csr = true;
export async function load() {
return {
staticContent: 'This is rendered on server',
dynamicData: null
};
}
src/routes/+page.svelte:
svelte
<script>
import { onMount } from 'svelte';
let { data } = $props();
let dynamicData = null;
onMount(async () => {
const response = await fetch('/api/dynamic');
dynamicData = await response.json();
});
</script>
<article>
<h1>{data.staticContent}</h1>
{#if dynamicData}
<p>{dynamicData.message}</p>
{:else}
<p>Loading dynamic content...</p>
{/if}
</article>
5.2 条件渲染 #
src/routes/+page.svelte:
svelte
<script>
import { browser } from '$app/environment';
let { data } = $props();
</script>
<h1>{data.title}</h1>
{#if browser}
<ClientOnlyComponent />
{:else}
<div class="placeholder">Loading...</div>
{/if}
六、SEO 优化 #
6.1 页面元数据 #
src/routes/+page.svelte:
svelte
<script>
import { page } from '$app/stores';
let { data } = $props();
</script>
<svelte:head>
<title>{data.title} | My Site</title>
<meta name="description" content={data.description} />
<meta property="og:title" content={data.title} />
<meta property="og:description" content={data.description} />
<meta property="og:image" content={data.image} />
<meta property="og:url" content={$page.url.href} />
<meta name="twitter:card" content="summary_large_image" />
</svelte:head>
<h1>{data.title}</h1>
6.2 动态元数据 #
src/routes/blog/[slug]/+page.ts:
typescript
export async function load({ params }) {
const post = await getPost(params.slug);
return {
post,
title: post.title,
description: post.excerpt,
image: post.coverImage
};
}
6.3 结构化数据 #
src/routes/blog/[slug]/+page.svelte:
svelte
<script>
let { data } = $props();
const structuredData = {
"@context": "https://schema.org",
"@type": "Article",
"headline": data.post.title,
"image": data.post.coverImage,
"author": {
"@type": "Person",
"name": data.post.author.name
},
"datePublished": data.post.publishedAt,
"dateModified": data.post.updatedAt
};
</script>
<svelte:head>
<script type="application/ld+json">
{JSON.stringify(structuredData)}
</script>
</svelte:head>
6.4 规范链接 #
src/routes/+page.svelte:
svelte
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<link rel="canonical" href={$page.url.origin + $page.url.pathname} />
</svelte:head>
七、性能优化 #
7.1 流式渲染 #
src/routes/+page.server.ts:
typescript
export async function load() {
return {
posts: loadPosts(),
comments: loadComments()
};
}
async function loadPosts() {
await delay(1000);
return [{ id: 1, title: 'Post 1' }];
}
async function loadComments() {
await delay(2000);
return [{ id: 1, text: 'Comment 1' }];
}
7.2 数据预取 #
src/routes/+layout.svelte:
svelte
<script>
let { children, data } = $props();
</script>
<nav>
<a href="/" data-sveltekit-preload-data="hover">Home</a>
<a href="/blog" data-sveltekit-preload-data="hover">Blog</a>
</nav>
{@render children()}
7.3 图片优化 #
svelte
<script>
import { enhanceImg } from '@sveltejs/enhanced-img';
</script>
<enhance-img
src="/images/hero.jpg"
alt="Hero image"
widths={[400, 800, 1200]}
sizes="100vw"
/>
八、部署配置 #
8.1 静态部署 #
svelte.config.js:
javascript
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: 'index.html',
precompress: true
})
}
};
8.2 Node.js 部署 #
svelte.config.js:
javascript
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter({
out: 'build',
precompress: true
})
}
};
8.3 Vercel 部署 #
svelte.config.js:
javascript
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'edge'
})
}
};
8.4 Cloudflare 部署 #
svelte.config.js:
javascript
import adapter from '@sveltejs/adapter-cloudflare';
export default {
kit: {
adapter: adapter({
routes: {
include: ['/*'],
exclude: ['<all>']
}
})
}
};
九、完整示例 #
9.1 博客页面配置 #
src/routes/blog/[slug]/+page.ts:
typescript
export const ssr = true;
export const csr = true;
export const prerender = true;
export async function entries() {
const posts = await getAllPosts();
return posts.map(post => ({ slug: post.slug }));
}
export async function load({ params }) {
const post = await getPost(params.slug);
if (!post) {
throw error(404, 'Post not found');
}
return {
post,
title: post.title,
description: post.excerpt,
image: post.coverImage
};
}
src/routes/blog/[slug]/+page.svelte:
svelte
<script>
import { page } from '$app/stores';
let { data } = $props();
</script>
<svelte:head>
<title>{data.post.title} | My Blog</title>
<meta name="description" content={data.post.excerpt} />
<meta property="og:title" content={data.post.title} />
<meta property="og:description" content={data.post.excerpt} />
<meta property="og:image" content={data.post.coverImage} />
<meta property="og:url" content={$page.url.href} />
<link rel="canonical" href={$page.url.href} />
</svelte:head>
<article>
<header>
<h1>{data.post.title}</h1>
<time datetime={data.post.publishedAt}>
{new Date(data.post.publishedAt).toLocaleDateString()}
</time>
</header>
<div class="content">
{data.post.content}
</div>
</article>
十、总结 #
| 配置 | 说明 |
|---|---|
ssr = true |
启用服务端渲染 |
ssr = false |
禁用服务端渲染 |
csr = true |
启用客户端路由 |
csr = false |
禁用客户端路由 |
prerender = true |
预渲染为静态页面 |
渲染模式选择:
- SSR:内容页面、SEO 重要页面
- SSG:博客、文档、营销页面
- CSR:登录后页面、交互密集页面
- 混合:静态外壳 + 动态内容
SSR 要点:
- 使用
ssr和csr配置渲染模式 prerender用于静态生成- 使用
svelte:head添加 SEO 元数据 - 根据页面需求选择合适的渲染模式
- 合理使用预取提升用户体验
最后更新:2026-03-28