Vercel 部署 SvelteKit 完整教學|2025 全端開發上線指南

Vercel 部署 SvelteKit 完整教學|2025 全端開發上線指南

SvelteKit 是 Svelte 的官方全端框架。

編譯時框架、極小 bundle、極快效能。

部署到 Vercel,讓你的 SvelteKit 應用發揮最大潛力。

這篇文章教你如何把 SvelteKit 應用部署到 Vercel。


SvelteKit + Vercel 的優勢

SvelteKit 特點

特點 說明
編譯時框架 沒有 Virtual DOM
極小 Bundle 比 React/Vue 小很多
內建 SSR/SSG 彈性渲染模式
檔案式路由 直覺的路由系統
表單 Actions 內建表單處理

Vercel 整合

  • 官方 Adapter 支援
  • 自動偵測專案
  • Edge Functions 支援
  • Serverless Functions 支援

專案準備

SvelteKit 專案結構

my-sveltekit-app/
├── src/
│   ├── lib/
│   │   └── components/
│   ├── routes/
│   │   ├── +page.svelte
│   │   ├── +page.server.ts
│   │   ├── +layout.svelte
│   │   └── api/
│   │       └── hello/
│   │           └── +server.ts
│   ├── app.html
│   └── app.d.ts
├── static/
├── svelte.config.js
├── vite.config.ts
└── package.json

package.json

{
  "name": "my-sveltekit-app",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "@sveltejs/adapter-vercel": "^5.0.0",
    "@sveltejs/kit": "^2.5.0",
    "svelte": "^4.2.0",
    "vite": "^5.0.0"
  }
}

本機測試

# 執行 build
npm run build

# 預覽
npm run preview

安裝 Vercel Adapter

安裝

npm install @sveltejs/adapter-vercel

設定 Adapter

// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),
  },
};

export default config;

Adapter 選項

import adapter from '@sveltejs/adapter-vercel';

const config = {
  kit: {
    adapter: adapter({
      // Edge Functions
      runtime: 'edge',

      // 區域設定
      regions: ['iad1', 'sfo1'],

      // 分開每個路由到獨立 Function
      split: false,
    }),
  },
};

export default config;

部署到 Vercel

方法一:從 GitHub 部署

步驟一:推送到 GitHub

git init
git add .
git commit -m "Initial commit"
gh repo create my-sveltekit-app --public --source=. --push

步驟二:連接 Vercel

  1. 前往 vercel.com
  2. 點擊 Add NewProject
  3. 選擇 Repository
  4. 點擊 Import

步驟三:確認設定

Vercel 自動偵測 SvelteKit:

設定項目
Framework SvelteKit
Build Command npm run build
Output Directory .vercel/output

方法二:Vercel CLI

npm install -g vercel
vercel login
vercel

渲染模式

SSR(預設)

<!-- src/routes/+page.svelte -->
<script>
  export let data;
</script>

<h1>{data.title}</h1>
// src/routes/+page.server.ts
export async function load() {
  return {
    title: 'Hello from SSR!',
  };
}

SSG(靜態生成)

// src/routes/+page.server.ts
export const prerender = true;

export async function load() {
  return {
    title: 'Static page',
  };
}

CSR(客戶端渲染)

// src/routes/+page.ts
export const ssr = false;

export async function load({ fetch }) {
  const res = await fetch('/api/data');
  return res.json();
}

全站設定

// src/routes/+layout.server.ts
export const prerender = true; // 全站靜態生成

環境變數

SvelteKit 環境變數

公開變數(會暴露給前端):

# .env
PUBLIC_API_URL=https://api.example.com
// 前端和後端都可用
import { PUBLIC_API_URL } from '$env/static/public';

私密變數(只在伺服器端):

# .env
API_SECRET=your-secret-key
// 只在伺服器端可用
import { API_SECRET } from '$env/static/private';

動態環境變數

// 動態讀取(執行時)
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';

const secret = env.API_SECRET;
const apiUrl = publicEnv.PUBLIC_API_URL;

在 Vercel 設定

  1. 專案 Settings → Environment Variables
  2. 新增變數
Name: PUBLIC_API_URL
Value: https://api.example.com

Name: API_SECRET
Value: your-secret-key

API Routes

建立 API 端點

// src/routes/api/hello/+server.ts
import { json } from '@sveltejs/kit';

export async function GET() {
  return json({
    message: 'Hello from SvelteKit!',
  });
}

訪問: https://your-app.vercel.app/api/hello

POST 請求

// src/routes/api/contact/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const POST: RequestHandler = async ({ request }) => {
  const data = await request.json();

  // 處理資料...

  return json({ success: true });
};

動態路由

// src/routes/api/users/[id]/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ params }) => {
  const { id } = params;

  return json({
    userId: id,
    name: `User ${id}`,
  });
};

錯誤處理

// src/routes/api/data/+server.ts
import { json, error } from '@sveltejs/kit';

export async function GET() {
  try {
    const data = await fetchData();
    return json(data);
  } catch (e) {
    throw error(500, 'Internal Server Error');
  }
}

Form Actions

基本表單處理

<!-- src/routes/contact/+page.svelte -->
<form method="POST">
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">送出</button>
</form>
// src/routes/contact/+page.server.ts
import type { Actions } from './$types';

export const actions: Actions = {
  default: async ({ request }) => {
    const formData = await request.formData();
    const name = formData.get('name');
    const email = formData.get('email');
    const message = formData.get('message');

    // 處理表單...
    await sendEmail({ name, email, message });

    return { success: true };
  },
};

多個 Actions

<!-- src/routes/todos/+page.svelte -->
<form method="POST" action="?/create">
  <input name="text" />
  <button>新增</button>
</form>

{#each data.todos as todo}
  <form method="POST" action="?/delete">
    <input type="hidden" name="id" value={todo.id} />
    <span>{todo.text}</span>
    <button>刪除</button>
  </form>
{/each}
// src/routes/todos/+page.server.ts
export const actions: Actions = {
  create: async ({ request }) => {
    const formData = await request.formData();
    const text = formData.get('text');
    await db.todos.create({ text });
  },

  delete: async ({ request }) => {
    const formData = await request.formData();
    const id = formData.get('id');
    await db.todos.delete(id);
  },
};

Edge Functions

路由層級 Edge

// src/routes/fast/+page.server.ts
export const config = {
  runtime: 'edge',
};

export async function load() {
  return {
    message: 'From the Edge!',
  };
}

API 端點 Edge

// src/routes/api/geo/+server.ts
import { json } from '@sveltejs/kit';

export const config = {
  runtime: 'edge',
};

export async function GET({ request }) {
  const country = request.headers.get('x-vercel-ip-country');
  const city = request.headers.get('x-vercel-ip-city');

  return json({ country, city });
}

全站 Edge

// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';

const config = {
  kit: {
    adapter: adapter({
      runtime: 'edge',
    }),
  },
};

效能優化

預載入資料

<!-- src/routes/+layout.svelte -->
<script>
  import { preloadData } from '$app/navigation';
</script>

<a
  href="/about"
  on:mouseenter={() => preloadData('/about')}
>
  關於我們
</a>

延遲載入元件

<script>
  import { onMount } from 'svelte';

  let HeavyComponent;

  onMount(async () => {
    const module = await import('$lib/components/Heavy.svelte');
    HeavyComponent = module.default;
  });
</script>

{#if HeavyComponent}
  <svelte:component this={HeavyComponent} />
{:else}
  <p>Loading...</p>
{/if}

串流載入

// src/routes/+page.server.ts
export async function load() {
  return {
    // 立即返回
    title: 'My Page',

    // 串流載入(不阻塞)
    comments: loadComments(),
  };
}

async function loadComments() {
  await new Promise(r => setTimeout(r, 2000));
  return [{ text: 'Comment 1' }, { text: 'Comment 2' }];
}
<!-- src/routes/+page.svelte -->
<script>
  export let data;
</script>

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

{#await data.comments}
  <p>Loading comments...</p>
{:then comments}
  {#each comments as comment}
    <p>{comment.text}</p>
  {/each}
{/await}

設定快取

// src/routes/api/data/+server.ts
import { json } from '@sveltejs/kit';

export async function GET() {
  return json(
    { data: 'cached' },
    {
      headers: {
        'Cache-Control': 'public, max-age=3600',
      },
    }
  );
}

常見問題排解

問題一:Build 失敗

錯誤訊息:

Error: @sveltejs/adapter-vercel requires a newer version of SvelteKit

解決方法:

npm update @sveltejs/kit @sveltejs/adapter-vercel

問題二:環境變數讀不到

檢查項目:

  1. 公開變數是否有 PUBLIC_ 前綴
  2. 是否使用正確的 import
// ✅ 正確
import { PUBLIC_API_URL } from '$env/static/public';
import { API_SECRET } from '$env/static/private';

// ❌ 錯誤
const apiUrl = process.env.PUBLIC_API_URL;

問題三:Form Action 404

可能原因:

表單沒有設定 method="POST"

<!-- ✅ 正確 -->
<form method="POST">

<!-- ❌ 錯誤 -->
<form>

問題四:SSR 水合錯誤

常見原因:

在伺服器端使用了 browser-only API

// ❌ 錯誤
const width = window.innerWidth;

// ✅ 正確
import { browser } from '$app/environment';

let width;
if (browser) {
  width = window.innerWidth;
}

問題五:API 路由 405

原因: 沒有 export 對應的 HTTP 方法

// 如果只 export GET,POST 會返回 405
export async function GET() { ... }
export async function POST() { ... } // 需要明確 export

認證整合

使用 Hooks

// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // 檢查 session
  const session = event.cookies.get('session');

  if (session) {
    const user = await validateSession(session);
    event.locals.user = user;
  }

  return resolve(event);
};

保護路由

// src/routes/admin/+page.server.ts
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ locals }) => {
  if (!locals.user) {
    throw redirect(303, '/login');
  }

  return {
    user: locals.user,
  };
};

部署檢查清單

部署前

  • [ ] npm run build 成功
  • [ ] 本機預覽正常
  • [ ] 環境變數已設定
  • [ ] Vercel Adapter 正確安裝
  • [ ] 沒有 browser-only 錯誤

部署後

  • [ ] 頁面正常顯示
  • [ ] SSR/SSG 正確運作
  • [ ] API 端點可訪問
  • [ ] Form Actions 正常
  • [ ] 環境變數正確

常見問題 FAQ

Q1:SvelteKit 和 Svelte 有什麼不同?

Svelte: 前端框架/編譯器

SvelteKit: 基於 Svelte 的全端框架

SvelteKit = Svelte + 路由 + SSR + API

Q2:應該用 SSR 還是 SSG?

選擇 SSR 選擇 SSG
動態內容 靜態內容
即時資料 不常更新
需要認證 公開內容

Q3:SvelteKit 效能好嗎?

非常好:

  • 編譯時優化
  • 極小的 bundle
  • 沒有 Virtual DOM 開銷

Q4:可以只用 SSG 嗎?

可以,使用 adapter-static

npm install @sveltejs/adapter-static

但如果要用 API Routes,還是需要 adapter-vercel

Q5:免費方案夠用嗎?

對大多數專案夠用:

  • SSR Functions 有執行限制
  • 頻寬 100 GB/月
  • Build 分鐘 6,000/月

Vercel 部署失敗?

Build Error、環境變數、自訂網域,我們幫你快速排除問題。

解決 Vercel 問題


延伸閱讀

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們