服务端渲染 #

一、渲染模式概述 #

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 要点:

  • 使用 ssrcsr 配置渲染模式
  • prerender 用于静态生成
  • 使用 svelte:head 添加 SEO 元数据
  • 根据页面需求选择合适的渲染模式
  • 合理使用预取提升用户体验
最后更新:2026-03-28