Vercel 部署 Astro 与 SvelteKit 实战:内容站点与交互应用上云指南

详解 Astro Islands 架构和 SvelteKit 在 Vercel 上的部署:Astro 静态/SSR/混合模式选择、Vercel Adapter 配置、SvelteKit Edge vs Node 运行时、图片优化与性能调优。附两种框架的完整部署配置。

Astro 和 SvelteKit 是近年增长最快的两个现代前端框架:Astro 以"零 JS 默认"的 Islands 架构统治内容站点,SvelteKit 以极致编译时优化和最小运行时成为高交互应用的利器。两者都原生支持 Vercel 部署——Astro 有官方 @astrojs/vercel 适配器,SvelteKit 有 @sveltejs/adapter-vercel。本文分别讲解两个框架在 Vercel 上的完整部署流程与优化策略。


第一部分:Astro 在 Vercel 上部署

一、Astro 架构特点与 Vercel 适配

Astro 的核心理念:

  • 默认零 JS:页面初始加载时,HTML 是纯静态的,没有客户端 JavaScript
  • Islands 架构:只有在需要交互的地方,才注入对应组件的 JS(React、Vue、Svelte、Solid 等都可以)
  • 内容优先:博客、文档、电商展示页、营销页的最佳选择
  • 混合渲染:支持 static(SSG)、server(SSR)、hybrid(混合)三种输出模式

Astro 在 Vercel 上的部署适配

输出模式Vercel 行为适用场景
static纯静态 HTML/CSS/JS,上传到 Vercel CDN博客、文档、营销页
serverSSR 渲染,每个请求通过 Vercel Serverless Function登录态页面、实时数据
hybrid大部分静态,部分动态路由走 SSR内容站带少量动态内容

二、项目初始化与配置

2.1 创建 Astro 项目

# 使用官方模板
npm create astro@latest my-astro-site
# 选择:
#   - Template: 推荐 "Astro Basics" 或 "Blog"
#   - TypeScript: 是
#   - Install dependencies: 是
#   - Initialize git: 是

cd my-astro-site
npm install

2.2 安装 Vercel 适配器

# Astro 3.0+ 推荐安装
npm install -D @astrojs/vercel
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';

export default defineConfig({
  output: 'static', // 或 'server' / 'hybrid'
  adapter: vercel(),
});

适配器会自动处理:

  • 静态文件上传到 Vercel CDN
  • SSR 路由生成 functions/ 目录并打包为 Vercel Serverless Functions
  • 图片优化对接 Vercel Image Optimization API

2.3 三种输出模式详解

模式一:Static(静态生成)

// astro.config.mjs
export default defineConfig({
  output: 'static',
  adapter: vercel(),
});

构建时生成纯 HTML 文件:

dist/
├── index.html
├── about/
│   └── index.html
├── blog/
│   ├── hello-world/
│   │   └── index.html
│   └── index.html
  • 最快:Vercel CDN 直接服务 HTML,无 Serverless 冷启动
  • 最便宜:不产生 Functions 调用费用
  • 最适合:博客、文档、营销站

模式二:Server(服务端渲染)

// astro.config.mjs
export default defineConfig({
  output: 'server',
  adapter: vercel({
    edgeMiddleware: false, // SSR 用 Node runtime
    functionPerRoute: true, // 每个路由一个 Function(推荐生产环境)
  }),
});

每个路由变成 Serverless Function:

.vercel/output/functions/
├── about.func/
│   └── index.js
├── blog.hello-world.func/
│   └── index.js
└── index.func/
    └── index.js
  • 可以读取 Cookie / Session / 请求头
  • 每次请求都通过 Vercel Function(有冷启动)
  • 适合登录页面、管理后台

模式三:Hybrid(混合)

// astro.config.mjs
export default defineConfig({
  output: 'hybrid',
  adapter: vercel(),
});
---
// src/pages/about.astro
// 默认静态生成
export const prerender = true;
---
<html><body><h1>About Us</h1></body></html>
---
// src/pages/dashboard.astro
// 强制 SSR
export const prerender = false;

const user = Astro.locals.user; // 从中间件获取
---
<html><body><h1>Welcome, {user.name}</h1></body></html>
  • 兼顾速度和动态能力
  • 静态页面直接 CDN,动态页面 Functions
  • 推荐大多数 Astro 项目使用

三、内容管理:Markdown + CMS

Astro 原生支持 Markdown / MDX:

npm install -D @astrojs/mdx
// astro.config.mjs
import mdx from '@astrojs/mdx';

export default defineConfig({
  integrations: [mdx()],
});

目录结构:

src/
├── content/
│   └── posts/
│       ├── hello-world.md
│       └── astro-guide.mdx
├── pages/
│   └── blog/
│       └── [slug].astro

Astro Content Collections(类型安全的内容管理):

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const posts = defineCollection({
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()),
    draft: z.boolean().default(false),
  }),
});

export const collections = { posts };
---
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('posts');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render();
---

<article>
  <h1>{post.data.title}</h1>
  <time>{post.data.date.toDateString()}</time>
  <Content />
</article>

四、图片优化

Astro 的 <Image> 组件在 Vercel 上自动对接 Vercel Image Optimization:

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---

<Image
  src={heroImage}
  alt="Hero"
  widths={[320, 640, 1280]}
  sizes="(max-width: 640px) 100vw, 50vw"
  quality={80}
/>
<!-- 外部图片 -->
<Image
  src="https://cdn.example.com/photo.jpg"
  alt="Photo"
  width={800}
  height={600}
  inferSize
/>

Vercel Image Optimization 会自动:

  • 生成多尺寸 WebP/AVIF
  • sizes 属性加载合适尺寸
  • CDN 缓存优化后的图片

五、部署验证

git push origin main

Vercel 自动检测 Astro 项目并部署。验证命令:

# 首页(应极速加载,纯 HTML)
curl -w "@curl-format.txt" https://my-astro-site.vercel.app/

# 博客文章页
curl https://my-astro-site.vercel.app/blog/hello-world

# 检查 TTFB
curl -s -o /dev/null -w "%{time_starttransfer}" https://my-astro-site.vercel.app/
# 期望值:< 200ms(静态页面 Cloudflare 加速后)

性能基准(Astro 静态页面 + Vercel + Cloudflare Proxy):

  • TTFB: 50-150ms
  • LCP: 800ms-1.5s(无 JS 干扰)
  • JS Bundle: 0-5KB(仅 Islands 注入的交互组件)

第二部分:SvelteKit 在 Vercel 上部署

一、SvelteKit 架构特点

SvelteKit 的核心优势:

  • 编译时优化:框架代码在构建时编译为高效的原生 JS,运行时极小
  • 文件路由src/routes/+page.svelte 自动成为页面路由
  • 多输出适配:同一套代码可以打包为 SSG、SSR、SPA、Edge Functions 等
  • 原生支持 Edge:SvelteKit 有第一方的 @sveltejs/adapter-vercel

二、项目初始化与 Vercel 适配

# 创建 SvelteKit 项目
npm create svelte@latest my-sveltekit-app
# 选择:
#   - Skeleton project
#   - TypeScript: Yes
#   - ESLint + Prettier: Yes

cd my-sveltekit-app
npm install

# 安装 Vercel 适配器
npm install -D @sveltejs/adapter-vercel
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter({
      // Edge Functions 模式
      runtime: 'edge',
      // 或 Node.js 模式:runtime: 'nodejs20.x'
    }),
  },
};

export default config;

三、Edge Runtime vs Node Runtime

SvelteKit @sveltejs/adapter-vercel 支持两种运行时:

维度Edge RuntimeNode.js Runtime
runtime 配置runtime: 'edge'runtime: 'nodejs20.x'
执行环境V8 Isolate(轻量)完整 Node.js
冷启动极快(< 5ms)中等(50-200ms)
Node API受限(无 fs、少量 npm 包)完整 Node.js 生态
数据库直连❌ 不支持(Prisma 需 Data Proxy)✅ 支持(Prisma/Postgres)
Bundle 大小极小较大
适用场景轻 API、中间件、A/B 测试、简单 SSR重 API、数据库交互、复杂后端逻辑

建议

  • 纯内容/展示型 SvelteKit 项目 → Edge Runtime(最快最省)
  • 需要数据库 + 复杂业务逻辑 → Node.js Runtime
  • 混合:API 路由用 Node,页面渲染用 Edge(edge = false 特定路由)

四、路由与数据获取

4.1 页面路由

<!-- src/routes/+page.svelte -->
<script>
  /** @type {import('./$types').PageData} */
  export let data;
</script>

<h1>{data.title}</h1>
<p>{data.description}</p>
// src/routes/+page.server.ts(服务端数据获取)
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async () => {
  // 这里可以查数据库或调用 API
  return {
    title: 'Welcome to SvelteKit',
    description: 'Deployed on Vercel',
  };
};

4.2 API 路由

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

export const GET: RequestHandler = async () => {
  // const users = await db.user.findMany();
  const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
  return json(users);
};

export const POST: RequestHandler = async ({ request }) => {
  const body = await request.json();
  // const newUser = await db.user.create({ data: body });
  return json({ created: true }, { status: 201 });
};

4.3 服务器钩子(Hooks)

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

export const handle: Handle = async ({ event, resolve }) => {
  // 认证检查
  const token = event.cookies.get('session');
  if (!token && event.url.pathname.startsWith('/dashboard')) {
    return new Response('Unauthorized', { status: 401 });
  }

  const response = await resolve(event);

  // 添加安全头
  response.headers.set('X-Frame-Options', 'DENY');

  return response;
};

4.4 环境变量

# .env
DATABASE_URL="postgresql://user:pass@host/db"
PUBLIC_API_BASE="https://api.example.com"
SECRET_KEY="your-secret"
// SvelteKit 环境变量规则:
// - PUBLIC_ 前缀:客户端 + 服务端都可访问
// - 无前缀:仅服务端
import { env } from '$env/dynamic/private';       // 服务端动态
import { PUBLIC_API_BASE } from '$env/static/public'; // 客户端静态

五、Edge 模式下的限制与解法

5.1 无法直接连接 Postgres

Edge Runtime 没有 TCP Socket,Prisma 直接连接会报错。解法:

npm install @prisma/client @prisma/extension-accelerate
// src/lib/db.ts(Edge-compatible)
import { PrismaClient } from '@prisma/client';
import { withAccelerate } from '@prisma/extension-accelerate';

const prisma = new PrismaClient().$extends(withAccelerate());

export { prisma };

使用 Prisma Data Proxy / Accelerate 通过 HTTP 连接数据库。

5.2 文件系统访问受限

Edge Runtime 没有 fs,不能读写本地文件:

// ❌ Edge 下不可用
import fs from 'fs';
fs.readFileSync('./data.json');

// ✅ 改用 fetch 从外部加载
const data = await fetch('https://cdn.example.com/data.json').then(r => r.json());

5.3 npm 包兼容性

部分原生 C++ 扩展的 npm 包(如 sharp 图片处理、bcrypt 加密)在 Edge 下不可用。改用:

不可用替代方案
sharp@vercel/image(托管)或 Cloudinary
bcryptbcryptjs(纯 JS 实现)
sqlite3better-sqlite3(Node only)或 Turso(Edge SQLite)

六、部署验证

git push origin main
# 测试首页 SSR
curl https://my-sveltekit-app.vercel.app

# 测试 API
curl https://my-sveltekit-app.vercel.app/api/users

# 测试 POST
curl -X POST https://my-sveltekit-app.vercel.app/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice"}'

七、Astro vs SvelteKit:在 Vercel 上怎么选?

维度AstroSvelteKit
静态内容性能⭐⭐⭐⭐⭐(零 JS 默认)⭐⭐⭐⭐(编译优化,但默认有 hydration)
交互应用⭐⭐⭐(Islands 架构够用)⭐⭐⭐⭐⭐(天生为富交互设计)
SSR 灵活性⭐⭐⭐(hybrid 模式够用)⭐⭐⭐⭐⭐(Server/SPA/Prerender/Edge 任意切换)
学习曲线⭐⭐⭐⭐⭐(接近纯 HTML)⭐⭐⭐⭐(概念清晰但需要时间)
Vercel 适配⭐⭐⭐⭐(官方适配器)⭐⭐⭐⭐⭐(官方第一方适配器 + Edge 支持)
生态组件React/Vue/Svelte 全兼容Svelte 生态为主
最佳场景博客、文档、营销、内容站SaaS、Dashboard、交互应用

一句话决策

  • 内容为王(文字多、图片多、交互少)→ Astro
  • 交互为王(表单、动画、状态管理多)→ SvelteKit
  • Vercel 两者都完美支持,选框架看项目类型。

常见问题(FAQ)

Astro 的 Islands 在 Vercel 上是怎么工作的?

Astro 构建时会把 Islands 组件(如 React、Svelte、Vue 组件)编译为独立的 JS chunk。页面 HTML 是纯静态的,只有当 Islands 组件进入视口时,对应的 JS chunk 才会懒加载执行。Vercel CDN 负责分发这些 chunk,和分发普通 JS 文件一样简单。

SvelteKit Edge 模式下能跑服务端渲染吗?

能。SvelteKit @sveltejs/adapter-vercelruntime: 'edge' 模式会让整站(页面渲染 + API 路由)都跑在 Vercel Edge Functions 上。SSR 的 load 函数在 Edge Runtime 中执行,但受限于 Edge 的 API 子集(如不能直接连 Postgres)。

Astro Hybrid 模式的 prerender = false 在 Vercel 上费用高吗?

不高。prerender = false 的页面走 Serverless Functions,但请求量通常不大(管理后台、用户中心)。纯静态页面(prerender = true)直接走 CDN,费用为 0。对于博客类项目,通常 95% 的请求都是静态页面。

SvelteKit 的 +page.server.ts+page.ts 在部署后有什么区别?

  • +page.server.ts:只在服务端执行(SSR 时运行一次),不会暴露到客户端,适合查数据库
  • +page.ts:在服务端和客户端都会执行(客户端 hydration 后再执行一次),适合调用外部 API
  • 在 Vercel 上,+page.server.ts 的代码打包在 Functions 中,+page.ts 的代码会出现在客户端 bundle 中

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「saas」更多文章