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 | 博客、文档、营销页 |
server | SSR 渲染,每个请求通过 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 View Transitions 与 Vercel 性能
Astro 3.0+ 引入了原生的 View Transitions API,让静态内容站点也能拥有 SPA 般的流畅页面切换体验。在 Vercel 上,这一特性与 CDN 缓存策略配合得天衣无缝。
4.1 启用 View Transitions
在 Astro 项目的布局组件中导入并启用:
---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>My Site</title>
<ViewTransitions />
</head>
<body>
<slot />
</body>
</html>
Astro 会自动处理:
- 页面切换时的淡入淡出动画
- 浏览器历史导航的前进 / 后退状态保持
<head>中部分元素的差异更新(如<title>、<meta>)
4.2 Vercel 上的性能预算
View Transitions 虽然提升了体验,但也引入了额外的 JS 开销。在 Vercel 上建议设置性能预算:
// astro.config.mjs
export default defineConfig({
output: 'static',
adapter: vercel(),
prefetch: {
prefetchAll: true, // 预取所有可见链接
defaultStrategy: 'viewport',
},
});
Prefetch 策略配合 Vercel CDN:
| 策略 | 行为 | Vercel 配合效果 |
|---|---|---|
hover | 鼠标悬停时开始加载 | 减少 TTFB 到 20-50ms |
viewport | 链接进入视口时预取 | 移动端体验最佳 |
load | 页面加载完成后预取 | SEO 权重高的页面适用 |
4.3 自定义过渡动画
---
// src/pages/blog/[slug].astro
import { fade, slide } from 'astro:transitions';
---
<main transition:animate={slide({ duration: '0.3s' })}>
<article transition:name={`post-${post.slug}`}>
<h1>{post.data.title}</h1>
<Content />
</article>
</main>
transition:name 为元素赋予唯一标识,Astro 在不同页面间匹配同名元素并执行 Morph 动画。在 Vercel 上,由于 CDN 缓存了 HTML,预取后的页面切换几乎是瞬间完成的。
注意事项:
- View Transitions 需要客户端 JS 支持,纯静态页面首次加载时无此功能
- 建议在文章列表页到详情页之间使用,提升阅读流体验
- 与
prefetchAll结合时,注意控制预取数据量,避免大量请求消耗 CDN 配额
五、图片优化
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 缓存优化后的图片
六、Astro 性能深度优化
Astro 的"零 JS 默认"让它在性能基准测试中常年名列前茅。在 Vercel 上部署时,通过以下几个层面的优化,可以把 Web Vitals 推向极致。
5.1 零 JS 默认与按需 Hydration
Astro 的核心哲学是:只有需要交互的组件才加载 JS。通过 client:* 指令精确控制 hydration 时机:
---
import Counter from '../components/Counter.jsx';
---
<!-- 页面加载完成后 hydrate -->
<Counter client:load />
<!-- 进入视口后再 hydrate -->
<Counter client:visible />
<!-- 媒体查询匹配时 hydrate -->
<Counter client:media="(min-width: 768px)" />
<!-- 只有 idle 时间才 hydrate -->
<Counter client:idle />
在 Vercel 上,client:visible 是最推荐的策略:静态 HTML 由 CDN 极速分发,JS chunk 只有在用户滚动到对应区域时才按需加载,最大限度减少首屏阻塞。
5.2 Islands 架构的 Bundle 分析
Astro 提供了内置的 bundle 分析命令:
# 构建并生成可视化分析报告
astro build --analyze
执行后会在 .astro/ 目录下生成 stats.html,用浏览器打开即可看到:
- 每个 Islands 组件对应的 JS chunk 大小
- 共享依赖(如 React、Vue runtime)的去重情况
- 未使用的死代码标记
优化建议:
- 单个 Islands chunk 控制在 30KB 以内(gzip 后)
- 共享依赖过多时,考虑统一用一种 UI 框架(如全部用 React)
- 对大型组件使用
client:only,跳过服务端渲染直接客户端挂载
5.3 关键 CSS 内联
Astro 构建时会自动提取关键 CSS 并内联到 <head> 中:
<head>
<!-- 内联的关键 CSS -->
<style>/* critical css */</style>
<!-- 非关键 CSS 异步加载 -->
<link rel="preload" href="/_astro/global.css" as="style" onload="this.rel='stylesheet'">
</head>
在 Vercel CDN 上,内联 CSS 意味着首包就包含所有渲染所需样式, eliminating render-blocking requests。可以通过 astro.config.mjs 中的 vite.build.cssCodeSplit 控制分割策略。
5.4 字体优化策略
内容站通常使用 Google Fonts 或自托管字体。推荐方案:
<!-- 使用 font-display: swap 避免 FOIT -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
或使用 Astro 的字体优化集成:
npm install astro-font
// astro.config.mjs
import { astroFont } from 'astro-font';
export default defineConfig({
integrations: [
astroFont({
families: [
{ name: 'Inter', styles: ['normal'], weights: [400, 600] },
],
}),
],
});
5.5 Web Vitals 基准表现
在 Vercel + Cloudflare 代理环境下,Astro 静态站点的典型数据:
| 指标 | 目标值 | Astro 实测值 |
|---|---|---|
| First Contentful Paint (FCP) | < 1.8s | 0.4-0.8s |
| Largest Contentful Paint (LCP) | < 2.5s | 0.8-1.5s |
| Time to Interactive (TTI) | < 3.8s | 0.5-1.0s |
| Cumulative Layout Shift (CLS) | < 0.1 | 0-0.02 |
| Total Blocking Time (TBT) | < 200ms | 0-10ms |
Zero JS 默认带来的 TBT 接近 0 是 Astro 的最大亮点——没有长时间任务阻塞主线程,页面在首次绘制后即可交互。
七、部署验证
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 Runtime | Node.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 |
| bcrypt | bcryptjs(纯 JS 实现) |
| sqlite3 | better-sqlite3(Node only)或 Turso(Edge SQLite) |
六、SvelteKit Runes 模式与 Vercel 部署
Svelte 5 引入了全新的 Runes 语法,用显式响应式原语取代了 Svelte 4 的编译时自动依赖追踪。在 Vercel 上部署 SvelteKit + Runes 时,需要理解其 SSR 层面的工作机制。
6.1 Runes 核心语法概览
<!-- src/routes/Counter.svelte -->
<script>
// Svelte 5 Runes:显式声明响应式状态
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
console.log('count changed to', count);
});
function increment() {
count += 1;
}
</script>
<button onclick={increment}>
Clicks: {count} (doubled: {doubled})
</button>
与 Svelte 4 的区别:
$state:替代let,显式标记响应式状态$derived:替代$:推导,编译器可更精确地追踪依赖$effect:替代afterUpdate/onMount,副作用管理更清晰
6.2 Runes 在 SSR 中的工作方式
在 Vercel 的 Serverless / Edge Functions 中,SvelteKit 的 SSR 会执行组件的 <script> 块。Runes 在 SSR 阶段的行为:
<script>
import { page } from '$app/stores';
// SSR 时:$state 初始值在服务端确定
let items = $state([]);
// $effect 不会在 SSR 阶段执行(它只在浏览器中运行)
$effect(() => {
document.title = `Loaded ${items.length} items`;
});
</script>
关键规则:
$state和$derived在服务端和客户端都会执行$effect和tick()只在客户端执行(SSR 时自动跳过)- Vercel Serverless Function 中,组件 SSR 结束后
$state的值会序列化到 HTML 中,客户端 hydrating 时恢复状态
6.3 Runes 与 Vercel Serverless Functions 的兼容性
Runes 编译后的输出是纯 JavaScript 函数调用,不依赖任何运行时 polyfill,因此与 Vercel 的运行时完全兼容:
| 运行时 | Runes 支持 | 注意事项 |
|---|---|---|
| Node.js 20.x | ✅ 完全支持 | 无限制 |
| Edge (V8 Isolate) | ✅ 完全支持 | 不依赖 Node API |
| Serverless | ✅ 完全支持 | 冷启动期间编译器已完成转换 |
// svelte.config.js(Runes + Vercel Edge)
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'edge',
}),
},
// Svelte 5 默认启用 Runes,无需额外配置
compilerOptions: {
runes: true,
},
};
6.4 从 Svelte 4 迁移到 5 的部署注意事项
# 1. 升级依赖
npm install svelte@next @sveltejs/kit@next
# 2. 检查编译警告
npm run build
# 关注:"$state rune used in Svelte 4 component" 等提示
迁移清单:
- 将
let x改为let x = $state(...)(需要响应式的变量) - 将
$: doubled = count * 2改为let doubled = $derived(count * 2) - 将
onMount(...)/afterUpdate(...)改为$effect(...) - 将
export let prop改为let { prop } = $props() - 测试 SSR:Vercel 预览环境中确认服务端渲染输出正确
6.5 SvelteKit Form Actions 与 Vercel 的协同
SvelteKit 的 Form Actions 是处理表单提交的最佳实践,在 Vercel 上部署时能充分利用 Progressive Enhancement:
// src/routes/contact/+page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const form = await request.formData();
const email = form.get('email');
// 服务端验证
if (!email || !email.includes('@')) {
return { success: false, error: 'Invalid email' };
}
// 提交到数据库或外部 API
// await db.subscribe({ email });
return { success: true };
},
};
<!-- src/routes/contact/+page.svelte -->
<script>
import { enhance } from '$app/forms';
/** @type {import('./$types').ActionData} */
export let form;
</script>
<form method="POST" use:enhance>
<input type="email" name="email" required />
<button type="submit">Subscribe</button>
{#if form?.success}
<p>Thank you for subscribing!</p>
{:else if form?.error}
<p class="error">{form.error}</p>
{/if}
</form>
Vercel 上的优势:
use:enhance让表单在 JS 禁用时也能正常提交(服务端回退)- JS 启用时,表单通过
fetch异步提交,页面无刷新 - Form Actions 的代码打包在 Vercel Functions 中,API 路由无需单独维护
七、部署验证
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 上怎么选?
| 维度 | Astro | SvelteKit |
|---|---|---|
| 静态内容性能 | ⭐⭐⭐⭐⭐(零 JS 默认) | ⭐⭐⭐⭐(编译优化,但默认有 hydration) |
| 交互应用 | ⭐⭐⭐(Islands 架构够用) | ⭐⭐⭐⭐⭐(天生为富交互设计) |
| SSR 灵活性 | ⭐⭐⭐(hybrid 模式够用) | ⭐⭐⭐⭐⭐(Server/SPA/Prerender/Edge 任意切换) |
| 学习曲线 | ⭐⭐⭐⭐⭐(接近纯 HTML) | ⭐⭐⭐⭐(概念清晰但需要时间) |
| Vercel 适配 | ⭐⭐⭐⭐(官方适配器) | ⭐⭐⭐⭐⭐(官方第一方适配器 + Edge 支持) |
| 生态组件 | React/Vue/Svelte 全兼容 | Svelte 生态为主 |
| 最佳场景 | 博客、文档、营销、内容站 | SaaS、Dashboard、交互应用 |
一句话决策:
- 内容为王(文字多、图片多、交互少)→ Astro
- 交互为王(表单、动画、状态管理多)→ SvelteKit
- Vercel 两者都完美支持,选框架看项目类型。
九、多框架混合部署策略
在真实的业务场景中, rarely 一个框架能满足所有需求。一个典型的 SaaS 产品可能需要:Astro 搭建营销官网 + SvelteKit 构建用户前台应用 + Next.js 管理后台。Vercel 支持在同一个 Team 中同时部署多个项目,并通过统一的域名策略和导航设计实现无缝协作。
9.1 架构设计示例
my-company-team (Vercel Team)
├── astro-marketing.vercel.app # Astro 静态站点(营销页、博客、文档)
├── app.my-company.com # SvelteKit 应用(用户前台、Dashboard)
├── admin.my-company.com # Next.js 管理后台
└── api.my-company.com # 独立的 API 服务(可选)
路由切分策略:
| 子域名 | 框架 | 输出模式 | 职责 |
|---|---|---|---|
www. / 根域 | Astro | static / hybrid | SEO Landing Page、博客、文档 |
app. | SvelteKit | server + Edge | 用户应用、Dashboard、交互功能 |
admin. | Next.js | server | 内部管理后台、数据报表 |
9.2 Monorepo 策略:Turborepo
Turborepo 是管理多框架 Monorepo 的首选工具:
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".vercel/output/**"]
},
"lint": {},
"test": {
"dependsOn": ["build"]
}
}
}
apps/
├── marketing/ # Astro 项目
│ ├── astro.config.mjs
│ └── src/
├── dashboard/ # SvelteKit 项目
│ ├── svelte.config.js
│ └── src/
└── admin/ # Next.js 项目
├── next.config.js
└── src/
packages/
├── ui/ # 共享 UI 组件(React + Svelte 双入口)
└── utils/ # 共享工具函数
每个 app 独立配置 Vercel 部署:
// apps/marketing/vercel.json
{
"buildCommand": "cd ../.. && turbo run build --filter=marketing",
"outputDirectory": "apps/marketing/dist",
"installCommand": "pnpm install"
}
9.3 Vercel 多项目共享环境变量
跨项目共享环境变量可以通过 Vercel CLI 或 Dashboard 的 “Shared Environment Variables” 功能:
# 在 Team 级别设置共享变量
vercel env add DATABASE_URL production --scope=my-team
vercel env add PUBLIC_API_BASE production --scope=my-team
# 链接到具体项目
vercel link --cwd apps/marketing
vercel env pull --cwd apps/marketing
敏感变量隔离原则:
- Team 级别共享:
PUBLIC_API_BASE、PUBLIC_ANALYTICS_ID(非敏感) - 项目级别私有:
DATABASE_URL、SECRET_KEY、STRIPE_SECRET(与项目强相关)
9.4 跨框架链接与导航的统一处理
当用户在 Astro 营销站点击 “Get Started” 跳转到 SvelteKit 应用时,需要保证用户体验的连续性:
---
// apps/marketing/src/components/Nav.astro
const APP_URL = import.meta.env.PUBLIC_APP_URL || 'https://app.example.com';
---
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
<a href={`${APP_URL}/login`} rel="noopener">Login</a>
<a href={`${APP_URL}/signup`} class="btn-primary">Get Started</a>
</nav>
统一设计系统:
如果多框架项目需要共享 Tailwind 配置和基础组件,可以抽离为 packages/ui:
// packages/ui/tailwind.config.ts
export default {
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
},
},
},
};
各项目引入:
// apps/marketing/tailwind.config.ts
import shared from '@mycompany/ui/tailwind';
export default {
...shared,
content: ['./src/**/*.{astro,html,js,jsx}'],
};
9.5 部署顺序与依赖管理
Turborepo 确保构建顺序正确:
# 根目录执行
npx turbo run build
# 输出顺序:
# 1. packages/ui (被依赖)
# 2. packages/utils (被依赖)
# 3. apps/marketing (独立)
# 4. apps/dashboard (独立)
# 5. apps/admin (独立)
Vercel Git 集成配置:为每个 app 单独创建 Vercel Project,指向同一个 Git 仓库的不同目录。推送代码时,Turborepo 的 Remote Caching 能显著加速 CI 构建。
十、部署后监控与优化
部署只是第一步,持续监控和优化才是保证生产环境稳定的关键。Vercel 提供了完善的监控工具,配合第三方服务可以建立完整的性能与错误追踪体系。
10.1 Core Web Vitals 实测对比
在相同的 Vercel + Cloudflare 代理环境下,Astro 和 SvelteKit 的典型表现对比:
| 指标 | Astro (static) | Astro (hybrid) | SvelteKit (Edge) | SvelteKit (Node) |
|---|---|---|---|---|
| TTFB | 50-100ms | 80-200ms | 10-30ms | 80-250ms |
| FCP | 0.4-0.8s | 0.5-1.0s | 0.3-0.6s | 0.4-0.8s |
| LCP | 0.8-1.2s | 1.0-1.5s | 0.6-1.0s | 0.8-1.3s |
| TBT | 0-10ms | 5-20ms | 10-50ms | 10-50ms |
| CLS | 0-0.02 | 0-0.05 | 0-0.03 | 0-0.03 |
| JS Bundle (首屏) | 0-5KB | 5-20KB | 15-40KB | 15-40KB |
解读:
- Astro static 在 TBT 和 JS Bundle 上无可匹敌,适合追求极致性能的内容站
- SvelteKit Edge 在 TTFB 上表现最佳(< 30ms),适合全球分布的实时应用
- Astro hybrid 和 SvelteKit Node 在动态能力上更强,适合复杂业务场景
10.2 Lighthouse 评分优化路径
达到 Lighthouse 95+ 的常见优化 checklist:
Astro 专属优化:
- 确保
<Image>组件设置了width+height(防止 CLS) - 使用
is:inline属性控制第三方脚本加载时机 - 启用
prefetch策略减少后续页面 TTFB - 对大型 Islands 使用
client:visible延迟 hydration - 定期运行
astro build --analyze检查 bundle 体积
SvelteKit 专属优化:
- 在
+layout.svelte中预加载核心路由数据 - 使用
+page.ts中的export const ssr = false对纯客户端页面禁用 SSR - 对 API 路由启用 HTTP Caching(
cache-control头) - Edge 模式下优先使用轻量 npm 包,减少 bundle 体积
通用优化:
- 启用 Vercel 的 Brotli / Gzip 压缩
- 为静态资源配置长期 Cache-Control
- 字体使用
font-display: swap - 图片延迟加载 + 响应式尺寸
10.3 Vercel Speed Insights 集成
Vercel Speed Insights 是免费的 Real User Monitoring (RUM) 工具,能采集真实用户的 Core Web Vitals 数据。
// Astro: 安装 @vercel/speed-insights
npm install @vercel/speed-insights
---
// src/layouts/BaseLayout.astro
import SpeedInsights from '@vercel/speed-insights/astro';
---
<html>
<head>
<SpeedInsights />
</head>
<body><slot /></body>
</html>
# SvelteKit: 安装 @vercel/speed-insights
npm install @vercel/speed-insights
// src/routes/+layout.svelte
import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
injectSpeedInsights();
部署后,在 Vercel Dashboard 的 “Speed Insights” 标签页查看:
- 各页面的 LCP、FID、CLS、INP 分布
- 按地域、设备类型、网络环境的细分数据
- 性能回退告警(自动检测 Core Web Vitals 恶化)
10.4 Sentry 错误追踪集成
生产环境必须配置错误监控。Sentry 与 Vercel 有原生集成:
# 安装 Sentry SDK
npm install @sentry/astro # Astro
npm install @sentry/sveltekit # SvelteKit
// Astro: astro.config.mjs
import { defineConfig } from 'astro/config';
import sentry from '@sentry/astro';
export default defineConfig({
integrations: [
sentry({
dsn: process.env.SENTRY_DSN,
sourceMapsUploadOptions: {
project: 'my-astro-site',
authToken: process.env.SENTRY_AUTH_TOKEN,
},
}),
],
});
// SvelteKit: src/hooks.client.ts
import * as Sentry from '@sentry/sveltekit';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
export const handleError = Sentry.handleErrorWithSentry();
Sentry 在 Vercel 上自动捕获:
- 服务端渲染异常(Vercel Functions 中的
throw) - 客户端 JS 运行时错误
- 性能追踪(API 调用耗时、数据库查询耗时)
- Session Replay(用户操作回放,定位复现困难的 bug)
常见问题(FAQ)
Astro 的 Islands 在 Vercel 上是怎么工作的?
Astro 构建时会把 Islands 组件(如 React、Svelte、Vue 组件)编译为独立的 JS chunk。页面 HTML 是纯静态的,只有当 Islands 组件进入视口时,对应的 JS chunk 才会懒加载执行。Vercel CDN 负责分发这些 chunk,和分发普通 JS 文件一样简单。
SvelteKit Edge 模式下能跑服务端渲染吗?
能。SvelteKit @sveltejs/adapter-vercel 的 runtime: '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 中
Astro 的 React/Vue/Svelte 组件 Islands 在 Vercel 上的加载行为是怎样的?
Astro Islands 组件在 Vercel CDN 上的加载遵循"按需、渐进"的原则:
- 构建阶段:Astro 将每个 Islands 组件编译为独立的 JS chunk(如
_astro/Counter.abc123.js),同时生成纯静态 HTML - 初始请求:Vercel CDN 返回 HTML,其中包含
<script type="module">标签,指向一个极小的 Islands 启动器(~1KB) - Hydration 触发:根据
client:*指令决定何时加载实际组件 chunkclient:load:DOMContentLoaded 后立即加载client:visible:Intersection Observer 检测到元素进入视口时加载client:media:CSS Media Query 匹配时加载client:idle:requestIdleCallback 触发时加载
- CDN 缓存:所有 JS chunk 被 Vercel CDN 长期缓存,二次访问直接从边缘节点获取
性能影响:一个页面如果有 5 个 React Islands 组件,HTML 中只有启动器脚本,五个 chunk 按各自策略独立加载。如果用户只浏览首屏不滚动,部分 chunk 可能永远不会被请求——这正是 Islands 架构节省带宽的核心机制。
SvelteKit 的 enhance 表单在 Vercel 上的 CSR 回退是如何工作的?
SvelteKit 的 use:enhance 是 Progressive Enhancement 的典范。在 Vercel 上,无论客户端 JS 处于什么状态,表单都能正常工作:
场景一:JS 完全禁用
- 表单通过标准 HTML
POST提交到 Vercel Functions - 服务端执行
+page.server.ts中的 Action - 返回包含
form数据的完整 HTML 页面 - 浏览器执行完整页面刷新,用户体验类似传统服务端渲染
场景二:JS 正常加载
use:enhance拦截表单 submit 事件- 通过
fetch将表单数据异步提交到同一个 Action endpoint - Action 返回 JSON 响应,页面局部更新
form状态 - 无页面刷新,用户体验如 SPA
场景三:JS 加载但 fetch 失败(网络断开 / Function 超时)
use:enhance的回调函数收到result对象,result.type === 'failure'- 默认行为是回退到标准表单提交(整页刷新)
- 可以通过自定义
enhance回调阻止回退,显示错误提示:
<form method="POST" use:enhance={({ formElement, cancel }) => {
return async ({ result, update }) => {
if (result.type === 'failure') {
// 阻止默认回退,自行处理错误
cancel();
toast.error('Submission failed. Please try again.');
} else {
await update();
}
};
}}>
在 Vercel 上,由于 Functions 冷启动可能导致 <form> 提交的首次请求较慢(200-500ms),建议在关键表单页面使用 prefetch 预热 Function。
Astro Content Layer 与 CMS 集成的 Vercel 适配怎么做?
Astro 的 Content Layer(Content Collections 的演进版)让外部 CMS 数据也能享受类型安全的内容管理。在 Vercel 上常见的 CMS 集成方案:
方案一:构建时拉取(推荐静态站点)
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const posts = defineCollection({
schema: z.object({
title: z.string(),
slug: z.string(),
publishedAt: z.date(),
}),
});
export const collections = { posts };
// src/lib/cms.ts
export async function fetchPostsFromCMS() {
const res = await fetch('https://api.headlesscms.com/posts', {
headers: { Authorization: `Bearer ${process.env.CMS_API_KEY}` },
});
return res.json();
}
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
output: 'static',
adapter: vercel(),
});
构建时 Astro 调用 CMS API,在 Vercel Build Step 中拉取全部内容生成静态页面。优点是:
- 内容变更通过 Webhook 触发 Vercel 重新部署
- 运行时 0 依赖,CDN 直接服务静态 HTML
- CMS downtime 不影响已部署站点
方案二:混合模式(部分内容实时获取)
---
// src/pages/blog/[slug].astro
export const prerender = true; // 文章正文静态
const { slug } = Astro.params;
const post = await getEntry('posts', slug);
// 评论实时获取(通过客户端 JS)
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
<!-- 评论组件:客户端获取 -->
<Comments postId={post.id} client:visible />
---
// src/components/Comments.astro(Islands)
const { postId } = Astro.props;
---
<div id={`comments-${postId}`}>
<!-- JS 加载后 fetch 评论 API -->
</div>
<script define:vars={{ postId }}>
fetch(`/api/comments?postId=${postId}`)
.then(r => r.json())
.then(comments => {
// 渲染评论列表
});
</script>
方案三:ISR / On-Demand Revalidation(Next.js 概念登录 Astro)
// 通过 Vercel API 触发按需重新构建
fetch('https://api.vercel.com/v13/deployments', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
},
body: JSON.stringify({
name: 'my-astro-site',
target: 'production',
}),
});
Astro 本身不支持 ISR(Incremental Static Regeneration),但可以通过 Webhook 触发全量重新部署实现类似效果。对于大型内容站点,全量构建可能较慢(几分钟),此时建议将内容拆分为多个独立集合分批构建。
SvelteKit 的 adapter-auto 与 adapter-vercel 应该怎么选?
@sveltejs/adapter-auto 是 SvelteKit 的默认适配器,它会根据部署平台自动检测并加载对应适配器。但在生产环境的 Vercel 部署中,推荐显式安装 @sveltejs/adapter-vercel:
| 维度 | adapter-auto | adapter-vercel |
|---|---|---|
| 安装方式 | 内置于 create-svelte | 需手动 npm install -D @sveltejs/adapter-vercel |
| 运行时选择 | 自动(通常选 Node) | 显式配置 edge / nodejs20.x |
| Edge 支持 | ❌ 不支持 | ✅ 原生支持 |
| 高级配置 | ❌ 无 | ✅ regions、split、isr 等 |
| bundle 分析 | ❌ 无 | ✅ 自动集成 Vercel Analytics |
| 构建输出 | 通用 | 针对 Vercel 优化(functions 目录结构) |
选择建议:
- 本地开发 / 多平台兼容测试 →
adapter-auto(零配置) - 生产部署到 Vercel →
adapter-vercel(功能完整 + 性能最优) - 需要 Edge Runtime → 必须用
adapter-vercel - 需要多区域部署 →
adapter-vercel的regions配置
// svelte.config.js(推荐生产配置)
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'edge',
regions: ['hkg1', 'sin1'], // 香港 + 新加坡
}),
},
};
相关阅读
- Vercel 详解:前端与 AI 应用的一站式云平台
- Vercel 部署 Nuxt.js (Vue) 实战
- Vercel 国内访问优化指南
- Vercel Edge Functions 深度指南
- Vercel 专题导航
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。