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 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.8s0.4-0.8s
Largest Contentful Paint (LCP)< 2.5s0.8-1.5s
Time to Interactive (TTI)< 3.8s0.5-1.0s
Cumulative Layout Shift (CLS)< 0.10-0.02
Total Blocking Time (TBT)< 200ms0-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 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)

六、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 在服务端和客户端都会执行
  • $effecttick() 只在客户端执行(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 上怎么选?

维度AstroSvelteKit
静态内容性能⭐⭐⭐⭐⭐(零 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. / 根域Astrostatic / hybridSEO Landing Page、博客、文档
app.SvelteKitserver + Edge用户应用、Dashboard、交互功能
admin.Next.jsserver内部管理后台、数据报表

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_BASEPUBLIC_ANALYTICS_ID(非敏感)
  • 项目级别私有DATABASE_URLSECRET_KEYSTRIPE_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)
TTFB50-100ms80-200ms10-30ms80-250ms
FCP0.4-0.8s0.5-1.0s0.3-0.6s0.4-0.8s
LCP0.8-1.2s1.0-1.5s0.6-1.0s0.8-1.3s
TBT0-10ms5-20ms10-50ms10-50ms
CLS0-0.020-0.050-0.030-0.03
JS Bundle (首屏)0-5KB5-20KB15-40KB15-40KB

解读:

  • Astro static 在 TBT 和 JS Bundle 上无可匹敌,适合追求极致性能的内容站
  • SvelteKit Edge 在 TTFB 上表现最佳(< 30ms),适合全球分布的实时应用
  • Astro hybridSvelteKit 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-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 中

Astro 的 React/Vue/Svelte 组件 Islands 在 Vercel 上的加载行为是怎样的?

Astro Islands 组件在 Vercel CDN 上的加载遵循"按需、渐进"的原则:

  1. 构建阶段:Astro 将每个 Islands 组件编译为独立的 JS chunk(如 _astro/Counter.abc123.js),同时生成纯静态 HTML
  2. 初始请求:Vercel CDN 返回 HTML,其中包含 <script type="module"> 标签,指向一个极小的 Islands 启动器(~1KB)
  3. Hydration 触发:根据 client:* 指令决定何时加载实际组件 chunk
    • client:load:DOMContentLoaded 后立即加载
    • client:visible:Intersection Observer 检测到元素进入视口时加载
    • client:media:CSS Media Query 匹配时加载
    • client:idle:requestIdleCallback 触发时加载
  4. 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-autoadapter-vercel
安装方式内置于 create-svelte需手动 npm install -D @sveltejs/adapter-vercel
运行时选择自动(通常选 Node)显式配置 edge / nodejs20.x
Edge 支持❌ 不支持✅ 原生支持
高级配置❌ 无regionssplitisr
bundle 分析❌ 无✅ 自动集成 Vercel Analytics
构建输出通用针对 Vercel 优化(functions 目录结构)

选择建议

  • 本地开发 / 多平台兼容测试adapter-auto(零配置)
  • 生产部署到 Verceladapter-vercel(功能完整 + 性能最优)
  • 需要 Edge Runtime → 必须用 adapter-vercel
  • 需要多区域部署adapter-vercelregions 配置
// svelte.config.js(推荐生产配置)
import adapter from '@sveltejs/adapter-vercel';

export default {
  kit: {
    adapter: adapter({
      runtime: 'edge',
      regions: ['hkg1', 'sin1'],  // 香港 + 新加坡
    }),
  },
};

相关阅读

继续阅读

探索更多技术文章

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

全部文章 返回首页

「saas」更多文章

  1. 短链接对 SEO 的影响与优化最佳实践
  2. UTM 参数 + 短链接:追踪每一条营销链路
  3. 私域流量运营中的短链接策略:从引流到转化