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
- 前往 vercel.com
- 點擊 Add New → Project
- 選擇 Repository
- 點擊 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 設定
- 專案 Settings → Environment Variables
- 新增變數
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
問題二:環境變數讀不到
檢查項目:
- 公開變數是否有
PUBLIC_前綴 - 是否使用正確的 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、環境變數、自訂網域,我們幫你快速排除問題。