Skip to content

Vercel & Netlify 部署

大白话解释: Vercel 和 Netlify 就像"前端项目的自动部署平台"。你把代码推到 GitHub,它们自动帮你构建、部署、配置 HTTPS、生成预览链接,全程不用手动操作。

为什么要用 Vercel/Netlify?

  • 零配置部署:不用自己买服务器、不用配置 Nginx、不用手动部署
  • 自动 CI/CD:推代码就自动部署,不用写 GitHub Actions
  • 预览部署:每个 PR 自动生成预览链接,方便测试
  • 免费额度:个人项目完全够用

Vercel vs Netlify 怎么选?

  • Vercel:Next.js 项目首选、Serverless Functions 更强、国内访问稍快
  • Netlify:静态站点更成熟、表单处理更方便、社区插件更多

什么时候用 Vercel/Netlify?

  • 个人项目、博客、文档站
  • 需要快速部署原型
  • 不想管理服务器
  • 需要预览部署功能

Vercel 和 Netlify 是前端最流行的静态站点部署平台,支持自动构建、预览部署、自定义域名、HTTPS 等功能。


Vercel

特点

  • Next.js 官方平台(Vercel 开发)
  • 零配置部署 Vue/React/Vite 项目
  • 自动 HTTPS、CDN 加速
  • 预览部署(PR 自动生成预览链接)
  • Serverless Functions 支持

部署方式

方式一:Git 集成(推荐)

  1. 登录 vercel.com
  2. 点击 "New Project"
  3. 导入 GitHub/GitLab 仓库
  4. 自动检测框架,点击 Deploy

方式二:CLI 部署

bash
# 安装 CLI
npm i -g vercel

# 登录
vercel login

# 部署(首次会交互式配置)
vercel

# 部署到生产环境
vercel --prod

Vercel CLI 常用命令

bash
# 环境变量管理
vercel env add <name>              # 添加环境变量(交互式选择环境)
vercel env add <name> production   # 仅添加到生产环境
vercel env ls                      # 列出所有环境变量
vercel env rm <name>               # 删除环境变量
vercel env pull .env.local         # 拉取环境变量到本地文件

# 域名管理
vercel domains add <domain>        # 添加自定义域名
vercel domains ls                  # 列出已绑定域名
vercel domains rm <domain>         # 移除域名

# 日志与调试
vercel logs <url>                  # 查看函数运行日志
vercel logs --follow               # 实时跟踪日志
vercel inspect <url>               # 查看部署详情(大小、函数、路由等)

# 部署管理
vercel ls                          # 列出项目部署记录
vercel promote <url>               # 将指定部署提升为生产环境
vercel rollback                    # 回滚到上一次生产部署
vercel rm <url>                    # 删除指定部署

# 项目配置
vercel link                        # 关联本地项目与 Vercel 项目
vercel env pull                    # 同步环境变量
vercel build                       # 本地模拟 Vercel 构建

vercel.json 配置

json
{
  "buildCommand": "yarn build",
  "outputDirectory": "dist",
  "framework": "vite"
}

Rewrites(重写规则)

重写不会改变 URL,仅将请求内部代理到目标路径,适合 SPA 路由和 API 代理。

json
{
  "rewrites": [
    { "source": "/old-path", "destination": "/new-path" },
    { "source": "/api/(.*)", "destination": "https://api.example.com/$1" },
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}
字段说明
source匹配路径(支持正则 (.*) 和命名捕获 :path
destination目标路径(可用 $1:path 引用捕获组)
has条件匹配(header、cookie、query)
missing缺失条件时匹配
json
{
  "rewrites": [
    {
      "source": "/blog/:slug",
      "has": [{ "type": "header", "key": "x-rewrite", "value": "true" }],
      "destination": "/blog-rewrite/:slug"
    },
    {
      "source": "/:path((?!api/).*)",
      "destination": "/index.html"
    }
  ]
}

Redirects(重定向规则)

重定向会改变浏览器 URL,返回 301/302 状态码。

json
{
  "redirects": [
    { "source": "/old-blog/:slug", "destination": "/blog/:slug", "permanent": true },
    { "source": "/docs", "destination": "/docs/intro", "statusCode": 302 },
    { "source": "/github", "destination": "https://github.com/user/repo" },
    {
      "source": "/api/:path*",
      "destination": "https://backend.example.com/:path*",
      "permanent": false,
      "has": [{ "type": "header", "key": "host", "value": "api.example.com" }]
    }
  ]
}
字段说明
permanenttrue 为 301 永久重定向,false 为 302 临时重定向
statusCode自定义状态码(301/302/307/308)
has仅在满足条件时触发重定向

Headers 配置

json
{
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    },
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
      ]
    },
    {
      "source": "/api/(.*)",
      "headers": [
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        { "key": "Access-Control-Allow-Methods", "value": "GET, POST, PUT, DELETE, OPTIONS" },
        { "key": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }
      ]
    }
  ]
}
常用 Header用途
Cache-Control静态资源长期缓存,HTML 不缓存
X-Frame-Options防止点击劫持(DENY / SAMEORIGIN
X-Content-Type-Options防止 MIME 嗅探(nosniff
Content-Security-Policy防止 XSS 攻击
Strict-Transport-Security强制 HTTPS(HSTS)

环境变量

bash
# 在 Vercel 控制台设置
# Settings → Environment Variables

# 或通过 CLI
vercel env add VITE_API_URL

# 拉取环境变量到本地
vercel env pull .env.local

预览部署

  • 每次 Push 到非 main 分支,自动生成预览 URL
  • PR 中会显示预览链接,方便代码审查
  • 可以在控制台关闭预览部署

Serverless Functions

Vercel 原生支持 Serverless Functions,放置在 api/ 目录下即可自动部署。

Node.js Functions

js
// api/hello.js — 最简单的 Serverless Function
// 放在 api/ 目录下,Vercel 自动识别为 API 端点
// 访问:GET /api/hello?name=Alice → { message: "Hello, Alice!" }
export default function handler(req, res) {
  const { name = 'World' } = req.query;  // 从 URL 查询参数获取 name,默认 "World"
  res.status(200).json({ message: `Hello, ${name}!` });  // 返回 JSON 响应
}
js
// api/user/[id].js — 动态路由(方括号语法)
// 访问:GET /api/user/123 → { userId: "123" }
// [id] 是动态参数,匹配 /api/user/xxx 的任意路径
export default function handler(req, res) {
  const { id } = req.query;              // 获取 URL 中的动态参数 id
  res.status(200).json({ userId: id });  // 返回用户 ID
}

Python Functions

python
# api/hello.py — Python Serverless Function
# Vercel 自动检测 .py 文件,使用 Python 运行时执行
# 访问:GET /api/hello?name=Alice → { "message": "Hello, Alice!" }
def handler(request):
    name = request.args.get("name", "World")  # 从查询参数获取 name,默认 "World"
    return {"message": f"Hello, {name}!"}      # 返回字典,自动序列化为 JSON

Go Functions

go
// api/hello.go — Go Serverless Function
package handler

import (
    "fmt"
    "net/http"
)

func Handler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")  // 从查询参数获取 name
    if name == "" {
        name = "World"                  // 默认值
    }
    // 写入 JSON 格式的响应
    fmt.Fprintf(w, `{"message":"Hello, %s!"}`, name)
}

Functions 配置

js
// api/data.js — 自定义 Function 配置
// 通过 export config 覆盖默认行为
export const config = {
  runtime: 'nodejs20',                 // 指定运行时版本(可选:nodejs18/20/22)
  maxDuration: 10,                     // 最大执行时间(秒),免费版上限 10s
  regions: ['iad1', 'sfo1'],           // 部署区域(减少延迟)
};
运行时支持版本冷启动时间
Node.js18 / 20 / 22~250ms
Python3.9 / 3.10 / 3.11~400ms
Go1.21+~100ms
Ruby3.2 / 3.3~500ms

Edge Functions

Edge Functions 运行在 CDN 边缘节点,延迟更低,适合做请求改写、A/B 测试、地理定向等。

ts
// middleware.ts(项目根目录)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const country = request.geo?.country || 'US';

  if (country === 'CN') {
    return NextResponse.redirect(new URL('/zh', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
ts
// api/edge-hello.ts
export const config = { runtime: 'edge' };

export default function handler(request: Request) {
  const url = new URL(request.url);
  const name = url.searchParams.get('name') || 'World';
  return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
    headers: { 'Content-Type': 'application/json' },
  });
}
特性Serverless FunctionsEdge Functions
运行位置区域服务器全球边缘节点
冷启动较慢(~250ms)极快(<50ms)
Node.js API完整子集(Web API)
最大执行时间60s(Pro)30s
适用场景数据库查询、复杂逻辑请求改写、认证、重定向

ISR / SSG / SSR 支持

Vercel 对 Next.js 的渲染模式提供全面支持:

模式说明Vercel 支持
SSG构建时生成静态页面默认支持
SSR每次请求服务端渲染自动适配
ISR增量静态再生成revalidate 参数控制
PPR部分预渲染(实验性)Next.js 15+ 支持
ts
// ISR 示例:每 60 秒重新生成页面
export const revalidate = 60;

export default async function Page() {
  const data = await fetch('https://api.example.com/posts');
  const posts = await data.json();
  return <PostList posts={posts} />;
}
ts
// 按需再生成(On-Demand Revalidation)
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';

export async function POST(request: Request) {
  const { path, tag } = await request.json();
  if (path) revalidatePath(path);
  if (tag) revalidateTag(tag);
  return Response.json({ revalidated: true });
}

Vercel Cron Jobs(定时任务)

Vercel Cron Jobs 可定时触发 Serverless Functions,适合数据同步、清理任务等。

json
// vercel.json
{
  "crons": [
    { "path": "/api/cron/daily", "schedule": "0 0 * * *" },
    { "path": "/api/cron/hourly", "schedule": "0 * * * *" },
    { "path": "/api/cron/weekly", "schedule": "0 0 * * 1" }
  ]
}
ts
// app/api/cron/daily/route.ts
import { NextResponse } from 'next/server';

export const dynamic = 'force-dynamic';
export const revalidate = 0;

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // 执行定时任务逻辑
  await syncDatabase();
  await cleanupExpiredData();

  return NextResponse.json({ success: true, executedAt: new Date().toISOString() });
}

💡 在 Vercel 控制台 Settings → Environment Variables 中设置 CRON_SECRET,Cron 请求会自动携带 Authorization: Bearer <CRON_SECRET> 头。

计划Cron 表达式说明
每天午夜0 0 * * *UTC 时间
每小时0 * * * *整点执行
每周一0 0 * * 1UTC 周一午夜
每月 1 号0 0 1 * *UTC 每月首日

💡 免费版最多 1 个 Cron Job,Pro 版最多 40 个。Cron 仅在生产环境生效。

Vercel Analytics

bash
# 安装
npm install @vercel/analytics
tsx
// app/layout.tsx 或 pages/_app.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
        <SpeedInsights />
      </body>
    </html>
  );
}
功能免费版Pro 版
页面浏览量
Web Vitals
自定义事件有限完整
实时数据
数据保留24 小时12 个月

Vercel 存储服务

服务用途说明
Vercel KV键值存储基于 Upstash Redis,适合会话、缓存
Vercel Postgres关系型数据库基于 Neon,Serverless PostgreSQL
Vercel Blob文件存储大文件上传、图片存储
ts
// Vercel KV 示例
import { kv } from '@vercel/kv';

await kv.set('user:1', JSON.stringify({ name: 'Alice' }));
const user = await kv.get('user:1');

// Vercel Postgres 示例
import { sql } from '@vercel/postgres';

const { rows } = await sql`SELECT * FROM users WHERE id = ${1}`;

// Vercel Blob 示例
import { put, del } from '@vercel/blob';

const blob = await put('avatar.png', file, { access: 'public' });
await del(blob.url);

Vercel 免费额度详细说明

资源Hobby(免费)ProEnterprise
带宽100GB/月1TB/月自定义
构建时间6,000 分钟/月24,000 分钟/月自定义
Serverless Functions 执行100GB-Hrs1000GB-Hrs自定义
Edge Functions 执行500,000 次/月5,000,000 次/月自定义
Cron Jobs1 个40 个自定义
团队成员1 人无限无限
预览部署
自定义域名50 个200 个自定义
Vercel KV3,000 请求/天150,000 请求/天自定义
Vercel Postgres256MB 存储512MB 存储自定义
Vercel Blob500MB 存储50GB 存储自定义
Analytics基础完整完整
项目数无限(个人)无限无限

⚠️ 超出免费额度后,Hobby 账户不会自动扣费,服务会被暂停。Pro 账户按量计费。

域名配置详细步骤

添加自定义域名

  1. 进入项目 → Settings → Domains
  2. 输入域名(如 www.example.com),点击 Add
  3. 选择重定向方式:
    • example.comwww.example.com(推荐)
    • www.example.comexample.com

DNS 配置方式

方式记录类型主机记录记录值
子域名CNAMEwwwcname.vercel-dns.com
根域名A@76.76.21.21
根域名(备选)A@76.76.21.142

Vercel DNS 托管

将域名 Nameservers 改为 Vercel 提供的 DNS 服务器,即可自动配置所有记录。

常见 DNS 服务商配置

text
Cloudflare:  CNAME www → cname.vercel-dns.com(关闭代理/橙色云朵)
阿里云万网:   CNAME www → cname.vercel-dns.com
腾讯云 DNSPod: CNAME www → cname.vercel-dns.com

构建缓存优化

Vercel 自动缓存 node_modules 和构建输出,可通过以下方式优化:

json
// vercel.json — 自定义安装和构建命令
{
  "framework": "vite",
  "buildCommand": "yarn build",
  "installCommand": "yarn install --frozen-lockfile"
}
bash
# 使用 Turborepo 缓存(Monorepo)
# turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}

Netlify 也支持类似缓存机制:

toml
# netlify.toml — 缓存优化
[build]
  command = "yarn build"

[build.environment]
  NETLIFY_CACHE_YARN = "true"    # 缓存 yarn 缓存目录
  NODE_OPTIONS = "--max-old-space-size=4096"  # 增大构建内存
优化手段VercelNetlify
依赖缓存自动缓存 node_modules自动缓存 node_modules
构建输出缓存自动(.next、dist 等)自动
Monorepo 缓存Turborepo 远程缓存Turborepo 远程缓存
缓存清理控制台 Redeploy(勾选 Clear cache)控制台 Deploys → Clear cache and deploy

Monorepo 支持

Vercel 原生支持 Monorepo,可指定 Root Directory 构建单个包。

monorepo/
├── apps/
│   ├── web/          ← Root Directory 设为 apps/web
│   └── admin/        ← Root Directory 设为 apps/admin
├── packages/
│   └── shared/
├── turbo.json
└── package.json
json
// turbo.json — Turborepo 配置
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
Monorepo 工具Vercel 支持
Turborepo✅ 原生(Vercel 开发)
pnpm Workspaces
Yarn Workspaces
Nx

Netlify

特点

  • 静态站点托管先驱
  • 支持表单处理、身份认证
  • 插件系统丰富
  • Split Testing(A/B 测试)

部署方式

方式一:Git 集成

  1. 登录 netlify.com
  2. 点击 "Add new site" → "Import an existing project"
  3. 选择仓库,配置构建命令

方式二:CLI 部署

bash
# 安装 CLI
npm i -g netlify-cli

# 登录
netlify login

# 初始化项目
netlify init

# 部署到预览环境
netlify deploy

# 部署到生产环境
netlify deploy --prod

# 仅部署 Functions
netlify deploy --prod --functions

# 仅部署指定目录
netlify deploy --dir=./dist

Netlify CLI 常用命令

bash
# 环境变量管理
netlify env:set <name> <value>            # 设置环境变量
netlify env:set <name> <value> --scope functions  # 仅 Functions 可用
netlify env:import .env                    # 从文件批量导入环境变量
netlify env:list                           # 列出所有环境变量
netlify env:unset <name>                   # 删除环境变量

# 站点管理
netlify open                               # 在浏览器打开站点
netlify open:admin                         # 打开 Netlify 控制台
netlify open:site                          # 打开站点 URL
netlify status                             # 查看当前站点状态和关联信息
netlify link                               # 关联本地项目与 Netlify 站点
netlify unlink                             # 取消关联

# 日志与调试
netlify logs:function <name>               # 查看指定函数日志
netlify logs:deploy                        # 查看部署日志

# 部署管理
netlify deploy:list                        # 查看部署历史
netlify deploy --trigger                   # 触发新的构建部署

# 本地开发
netlify dev                                # 启动本地开发服务器(模拟 Functions + 环境变量)
netlify dev --port 8888                    # 指定端口

方式三:拖拽部署

  • 直接将 dist 文件夹拖拽到 Netlify 控制台
  • 适合快速测试

netlify.toml 配置

toml
[build]
  command = "yarn build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "20"

# SPA 路由
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

# API 代理
[[redirects]]
  from = "/api/*"
  to = "https://api.example.com/:splat"
  status = 200

# 自定义域名重定向
[[redirects]]
  from = "https://old-domain.com/*"
  to = "https://new-domain.com/:splat"
  status = 301

# 缓存策略
[[headers]]
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/*.html"
  [headers.values]
    Cache-Control = "no-cache, no-store, must-revalidate"

_redirects 和 _headers 文件

除了 netlify.toml,Netlify 还支持在发布目录中使用 _redirects_headers 文件,优先级低于 netlify.toml

_redirects 文件

text
# 格式:源路径  目标路径  状态码(可选)  条件(可选)

# SPA 路由回退
/*  /index.html  200

# 301 永久重定向
/old-page  /new-page  301

# 带条件的重定向(仅特定国家)
/geo-blocked  /sorry  302  Country=CN,RU

# 带条件的重定向(仅特定角色,需 Identity)
/admin/*  /admin/:splat  200  Role=admin

# 外部代理
/api/*  https://api.example.com/:splat  200

# 签名重定向(防止篡改)
/secret-docs  /docs  302  Signed=true

# 404 自定义页面
/*  /404.html  404

💡 文件放在发布目录根目录(如 public/_redirectsdist/_redirects),构建时会自动复制。

_headers 文件

text
# 格式:
# 路径匹配
#   Header-Name: Header-Value

/assets/*
  Cache-Control: public, max-age=31536000, immutable

/*.html
  Cache-Control: no-cache, no-store, must-revalidate

/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin

/api/*
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
配置方式优先级适用场景
netlify.toml最高项目级配置,版本控制
_redirects / _headers 文件动态生成或由构建工具输出
UI 控制台最低快速调整,不进代码库

表单处理

html
<!-- Netlify 自动处理表单提交,无需后端 -->
<form name="contact" method="POST" data-netlify="true">
  <input type="hidden" name="form-name" value="contact" />
  <input type="text" name="name" />
  <input type="email" name="email" />
  <button type="submit">发送</button>
</form>

Netlify Functions

Netlify Functions 基于 AWS Lambda,放置在 netlify/functions/ 目录下。

js
// netlify/functions/hello.js
exports.handler = async (event, context) => {
  const name = event.queryStringParameters?.name || 'World';
  return {
    statusCode: 200,
    body: JSON.stringify({ message: `Hello, ${name}!` }),
  };
};
js
// netlify/functions/protected.js — 使用 context.clientContext 获取用户信息
exports.handler = async (event, context) => {
  const { user } = context.clientContext || {};
  if (!user) {
    return { statusCode: 401, body: 'Unauthorized' };
  }
  return {
    statusCode: 200,
    body: JSON.stringify({ user }),
  };
};
toml
# netlify.toml — Functions 配置
[functions]
  directory = "netlify/functions"
  node_bundler = "esbuild"    # 使用 esbuild 打包(更快)
  included_files = ["data/**"] # 包含额外文件
  external_node_modules = ["sharp"] # 外部模块
特性Netlify FunctionsVercel Functions
运行时AWS Lambda自有基础设施
最大执行时间10s(免费)/ 26min(Pro)10s(免费)/ 60s(Pro)
包大小限制50MB(未压缩)250MB
TypeScript
Scheduled Functions✅(Pro)✅(Cron)

Edge Functions

Netlify Edge Functions 基于 Deno 运行时,部署在全球边缘节点。

ts
// netlify/edge-functions/geo-redirect.ts
import type { Context } from '@netlify/edge-functions';

export default async (request: Request, context: Context) => {
  const country = context.geo.country?.code || 'US';

  if (country === 'CN') {
    return new Response('', {
      status: 302,
      headers: { Location: '/zh' },
    });
  }
};

export const config = { path: '/' };
ts
// netlify/edge-functions/ab-test.ts
import type { Context } from '@netlify/edge-functions';

export default async (request: Request, context: Context) => {
  const bucket = Math.random() < 0.5 ? 'a' : 'b';
  context.cookies.set('ab-test', bucket, { path: '/' });

  const url = new URL(request.url);
  url.pathname = `/variant-${bucket}${url.pathname}`;

  return context.rewrite(url);
};

export const config = { path: '/' };
toml
# netlify.toml — 注册 Edge Functions
[[edge_functions]]
  function = "geo-redirect"
  path = "/"

[[edge_functions]]
  function = "ab-test"
  path = "/landing"
特性Netlify Edge FunctionsVercel Edge Functions
运行时DenoEdge Runtime
全球节点数100+100+
最大执行时间50ms(CPU)30s
Geo APIcontext.georequest.geo
KV 存储Edge ContextEdge Config

身份认证(Identity)

Netlify 内置 Identity 服务,无需自建认证系统。

toml
# netlify.toml
[dev]
  autoLaunch = false

# Identity 默认开启,无需额外配置
js
// 客户端集成(使用 netlify-identity-widget)
import netlifyIdentity from 'netlify-identity-widget';

netlifyIdentity.init();

// 登录
netlifyIdentity.open('login');

// 注册
netlifyIdentity.open('signup');

// 获取当前用户
const user = netlifyIdentity.currentUser();

// 监听事件
netlifyIdentity.on('init', user => console.log('init', user));
netlifyIdentity.on('login', user => console.log('login', user));
netlifyIdentity.on('logout', () => console.log('logged out'));
js
// Functions 中验证用户
exports.handler = async (event, context) => {
  const { user } = context.clientContext;

  // user 包含:
  // - sub: 用户唯一 ID
  // - email: 邮箱
  // - app_metadata: 管理员设置的角色等
  // - user_metadata: 用户自定义信息

  return {
    statusCode: 200,
    body: JSON.stringify({ userId: user.sub, email: user.email }),
  };
};
功能免费版Pro 版
用户数1,00010,000+
GitHub/GitLab 登录
Google 登录
邮箱邀请
角色管理

表单处理详细用法

html
<!-- 通知表单 -->
<form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field">
  <input type="hidden" name="form-name" value="contact" />
  <p class="hidden"><input name="bot-field" /></p>
  <input type="text" name="name" placeholder="姓名" required />
  <input type="email" name="email" placeholder="邮箱" required />
  <textarea name="message" placeholder="留言" required></textarea>
  <button type="submit">发送</button>
</form>
js
// JavaScript 提交表单
const handleSubmit = async (event) => {
  event.preventDefault();
  const formData = new FormData(event.target);

  await fetch('/', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams(formData).toString(),
  });
};

表单通知集成

在 Netlify 控制台配置表单通知:

通知方式配置说明
EmailSettings → Forms → Form notifications → Email notification
Slack需要 Webhook URL
Discord需要 Webhook URL
Zapier连接第三方服务
Webhook自定义 HTTP 端点

表单 + Functions 联动

js
// netlify/functions/submission-created.js
// 表单提交时自动触发
exports.handler = async (event) => {
  const payload = JSON.parse(event.body);
  const { name, email, message } = payload.data;

  // 发送到自定义服务、数据库等
  console.log(`新表单提交: ${name} (${email}): ${message}`);

  return { statusCode: 200 };
};

Split Testing 详细配置

Netlify 原生支持分支级别的流量分割(A/B 测试)。

bash
# 使用 CLI 配置 Split Testing
netlify split:fractions --production-branch main --branch experiment-v2 --fraction 0.3

# 查看当前配置
netlify split:list

# 结束测试并选择获胜分支
netlify split:wins --branch main
toml
# netlify.toml — 也可通过配置文件设置
# 通常在控制台操作更方便
# Settings → Split Testing → 选择分支和流量比例
功能说明
流量分配自定义比例(如 70/30)
分支支持任意部署分支
Cookie 持久化同一用户始终看到同一版本
GA 集成可配合 Google Analytics 分析

Netlify 免费额度详细说明

资源Starter(免费)ProEnterprise
带宽100GB/月1TB/月自定义
构建时间300 分钟/月25,000 分钟/月自定义
Functions 执行125K 请求/月5M 请求/月自定义
Functions 执行时长100GB-Hrs/月500GB-Hrs/月自定义
Forms 提交100 次/月1,000 次/月自定义
Identity 用户1,000 人10,000 人自定义
团队成员1 人无限无限
自定义域名
Split Testing
插件数量
Deploy Previews
Slack 通知

⚠️ 超出免费带宽后站点不会暂停,但会收到警告邮件并要求升级。

Monorepo 支持

Netlify 支持 Monorepo,可在控制台设置 Base Directory 指定构建目录。

toml
# apps/web/netlify.toml
[build]
  base = "apps/web"
  command = "yarn build"
  publish = "dist"

# 或在 monorepo 根目录统一管理
# Settings → Build & deploy → Base directory 设为 apps/web
Monorepo 工具Netlify 支持
Turborepo
pnpm Workspaces
Yarn Workspaces
Nx

💡 Netlify 会自动检测 monorepo 中的 netlify.toml。多个子项目需分别创建站点,各自设置 Base Directory。

插件系统

Netlify 支持构建插件,可在构建生命周期中执行自定义逻辑。

bash
# 安装官方插件
npm install -D netlify-plugin-lighthouse

# 安装社区插件
npm install -D netlify-plugin-image-optim
toml
# netlify.toml — 注册插件
[[plugins]]
  package = "netlify-plugin-lighthouse"

  [plugins.inputs]
    audits = ["performance", "accessibility", "best-practices"]

[[plugins]]
  package = "netlify-plugin-image-optim"

  [plugins.inputs]
    quality = 80
js
// netlify/plugins/my-plugin/index.js — 自定义插件
module.exports = {
  onPreBuild: ({ utils }) => {
    console.log('构建前执行...');
  },
  onBuild: ({ utils }) => {
    console.log('构建中执行...');
  },
  onPostBuild: ({ utils }) => {
    console.log('构建后执行...');
  },
  onSuccess: ({ utils }) => {
    console.log('构建成功!');
  },
  onError: ({ utils }) => {
    console.log('构建失败!');
  },
};
热门插件功能
netlify-plugin-lighthouseLighthouse 性能审计
netlify-plugin-image-optim图片压缩优化
netlify-plugin-subfont字体优化
netlify-plugin-checklinks死链检测
netlify-plugin-cypressE2E 测试

对比

特性VercelNetlify
框架支持Next.js 最佳,其他也支持通用
构建速度
Serverless原生支持基于 AWS Lambda
Edge Functions原生(Edge Runtime)原生(Deno)
表单处理不支持原生支持
A/B 测试不支持原生 Split Testing
免费带宽100GB/月100GB/月
免费构建时间6,000 分钟/月300 分钟/月
免费函数调用100GB-Hrs125K 请求
Cron Jobs✅(免费 1 个)✅(Pro 版)
自定义域名
自动 HTTPS
预览部署
插件系统
身份认证❌(需第三方)✅(内置 Identity)
并发构建上限Hobby: 1 / Pro: 3Starter: 1 / Pro: 3
Monorepo✅ 原生支持✅ 设置 Base Directory
回滚方式CLI + 控制台CLI + 控制台

自定义域名配置

Vercel

  1. 进入项目 → Settings → Domains
  2. 添加域名(如 www.example.com
  3. 按提示配置 DNS:
    • CNAME 记录:wwwcname.vercel-dns.com
    • 或 A 记录:@76.76.21.21

Netlify

  1. 进入项目 → Domain settings → Add custom domain
  2. 配置 DNS:
    • CNAME 记录:wwwyour-site.netlify.app
    • 或使用 Netlify DNS(推荐)

常见问题

构建失败

bash
# 检查构建命令是否正确
# 检查 Node 版本
# 检查环境变量是否配置

路由 404

json
// Vercel: vercel.json
{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
toml
# Netlify: netlify.toml
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

环境变量不生效

bash
# Vite 项目:环境变量必须以 VITE_ 开头
# 确认在平台控制台设置了环境变量
# 确认构建时环境变量可用(不是运行时)

其他部署平台对比

Cloudflare Pages

Cloudflare Pages 托管在 Cloudflare 全球 CDN 上,与 Workers 深度集成。

bash
# 使用 Wrangler CLI 部署
npm install -g wrangler
wrangler pages project create my-app
wrangler pages deploy dist --project-name=my-app
特性Cloudflare Pages
免费带宽无限
构建次数500 次/月
ServerlessCloudflare Workers
边缘运行时V8 Isolates
KV 存储Workers KV
数据库D1(SQLite)、Hyperdrive
R2 存储S3 兼容对象存储
toml
# wrangler.toml — Workers 配置
name = "my-worker"
main = "src/worker.ts"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "CACHE"
id = "xxxxxxxxxxxx"

GitHub Pages

GitHub Pages 适合个人项目和文档站点,完全免费但功能有限。

yaml
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
  push:
    branches: [main]

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install && npm run build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
      - uses: actions/deploy-pages@v4
特性GitHub Pages
费用完全免费
带宽限制100GB/月
构建仅支持 Actions 或 gh-pages 分支
Serverless
自定义域名
HTTPS

Gitee Pages

Gitee Pages(Gitee Pages 服务)适合国内访问,但需实名认证。

特性Gitee Pages
费用免费(需实名认证)
访问速度国内快
自定义域名❌(仅支持 gitee.io)
HTTPS
自动部署❌(需手动更新)
限制内容审核、仓库公开

Railway

Railway 提供全栈部署能力,支持数据库、消息队列等后端服务。

bash
# 安装 CLI
npm install -g @railway/cli

# 登录
railway login

# 初始化项目
railway init

# 部署
railway up
特性Railway
免费额度$5/月
数据库PostgreSQL、MySQL、Redis、MongoDB
Docker 支持
自定义域名
自动 HTTPS
Cron Jobs

Render

Render 提供 Web 服务、静态站点、数据库、Cron Jobs 等全栈能力。

yaml
# render.yaml
services:
  - type: web
    name: my-app
    runtime: node
    buildCommand: yarn install && yarn build
    startCommand: yarn start
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: my-db
          property: connectionString

databases:
  - name: my-db
    plan: free
特性Render
免费额度静态站点免费,Web 服务 750h/月
数据库PostgreSQL(免费 90 天)
Docker 支持
Cron Jobs
自定义域名

腾讯云 Webify

腾讯云 Webify 针对国内前端项目优化,支持备案域名。

特性腾讯云 Webify
费用免费额度有限
国内访问✅(需备案)
框架支持Vue/React/Next.js/Nuxt
自定义域名✅(需备案)
CDN腾讯云 CDN

阿里云 Function Compute

阿里云函数计算适合 Serverless 全栈部署。

特性阿里云 Function Compute
计费方式按量付费
运行时Node.js/Python/Java/Go
API 网关
对象存储OSS
自定义域名✅(需备案)

平台选择建议

场景推荐平台
个人博客/文档GitHub Pages、Vercel
国内访问优先腾讯云 Webify、阿里云 FC
全栈应用Railway、Render
高流量站点Cloudflare Pages(无限带宽)
Next.js 项目Vercel(官方支持最佳)
预算敏感Cloudflare Pages、GitHub Pages

部署最佳实践

预览部署 + 评论机器人

预览部署让 PR 审查更高效,评论机器人自动在 PR 中发布预览链接。

yaml
# GitHub Actions — 自定义预览部署通知
name: Preview Deploy
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install && npm run build
      - name: Deploy to Vercel Preview
        run: npx vercel --token=${{ secrets.VERCEL_TOKEN }}
      - name: Comment PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚀 预览部署完成!\n\nhttps://preview-pr-${{ github.event.number }}.vercel.app'
            });

部署 Hooks(部署钩子)

部署钩子允许通过 HTTP 请求触发部署,适合外部系统联动。

Vercel Deploy Hooks

  1. 进入项目 → Settings → Git → Deploy Hooks
  2. 填入 Hook Name 和目标分支(如 main
  3. 生成的 URL 格式:https://api.vercel.com/v1/integrations/deploy/prj_xxxx/yyyy
bash
# 通过 curl 触发部署
curl -X POST "https://api.vercel.com/v1/integrations/deploy/prj_xxxx/yyyy"

Netlify Deploy Hooks

  1. 进入项目 → Site configuration → Build & deploy → Build hooks
  2. 填入名称和目标分支
  3. 生成的 URL 格式:https://api.netlify.com/build_hooks/xxxx
bash
# 通过 curl 触发部署
curl -X POST "https://api.netlify.com/build_hooks/xxxx"
场景VercelNetlify
CMS 内容更新触发Deploy HookBuild Hook
定时重建Cron + Deploy HookBuild Hook + 外部定时器
数据库变更触发Webhook → Deploy HookWebhook → Build Hook
GitHub Action 触发API + TokenAPI + Token

并发构建限制

计划VercelNetlify
免费版1 个并发构建1 个并发构建
Pro 版3 个并发构建3 个并发构建
Enterprise自定义自定义

💡 Monorepo 项目中多个子项目同时推送到同一仓库时,超出并发限制的构建会排队等待。

部署通知集成

js
// Vercel Deploy Hook — 部署成功后通知
// 在 Vercel 控制台 Settings → Git → Deploy Hooks 创建

// Webhook 接收端
app.post('/deploy-webhook', (req, res) => {
  const { type, payload } = req.body;
  if (type === 'deployment.succeeded') {
    sendSlackNotification(`部署成功: ${payload.deployment.url}`);
  } else if (type === 'deployment.error') {
    sendSlackNotification(`部署失败: ${payload.deployment.meta}`);
  }
  res.status(200).send('OK');
});
toml
# Netlify — 构建通知
# Settings → Build & deploy → Deploy notifications
# 支持 Email、Slack、Discord、Webhook

回滚策略

Vercel 回滚详细步骤

UI 操作:

  1. 进入项目 → Deployments
  2. 找到目标历史部署,点击进入详情
  3. 点击右上角 "..." → "Promote to Production"
  4. 确认后立即生效

CLI 操作:

bash
# 查看部署历史
vercel ls

# 回滚到上一次生产部署
vercel rollback

# 回滚到指定部署(需先通过 vercel ls 获取 URL)
vercel promote https://my-app-xxxxx.vercel.app

Git 回滚:

bash
# 回滚代码后重新部署
git revert HEAD
git push origin main
# Vercel 自动触发重新部署

Netlify 回滚详细步骤

UI 操作:

  1. 进入项目 → Deploys
  2. 找到目标历史部署
  3. 点击 "Publish deploy" 按钮
  4. 确认后立即生效

CLI 操作:

bash
# 查看部署历史
netlify deploy:list

# 回滚到指定部署(通过 deploy ID)
netlify api updateDeploy --data '{"deploy_id":"<deploy-id>","state":"ready"}'

Git 回滚:

bash
git revert HEAD
git push origin main
# Netlify 自动触发重新部署
平台UI 回滚CLI 回滚Git 回滚
VercelDeployments → Promote to Productionvercel rollback / vercel promoterevert + push
NetlifyDeploys → Publish deploynetlify deploy:listrevert + push

💡 两个平台的 UI 回滚都是即时生效的,不需要重新构建。Git 回滚会触发新的构建流程。

性能监控

bash
# Vercel — 使用 Speed Insights
npm install @vercel/speed-insights

# Netlify — 使用 Lighthouse 插件
npm install -D netlify-plugin-lighthouse
监控指标工具说明
Core Web VitalsVercel Analytics / PageSpeed InsightsLCP、FID、CLS
构建时间平台控制台监控构建耗时趋势
错误率Sentry / LogRocket前端异常监控
可用性UptimeRobot / Better Uptime站点可用性监控
函数冷启动平台控制台 + 自定义日志Serverless 性能优化参考
ts
// 自定义性能监控(轻量方案)
if (typeof window !== 'undefined') {
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      if (entry.entryType === 'largest-contentful-paint') {
        console.log('LCP:', entry.startTime);
      }
      if (entry.entryType === 'layout-shift') {
        console.log('CLS:', entry.value);
      }
    }
  });
  observer.observe({ type: 'largest-contentful-paint', buffered: true });
  observer.observe({ type: 'layout-shift', buffered: true });
}

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准