1. Vercel 部署
Vercel 由 Next.js 团队打造,是与 Next.js 契合度最高的托管平台:构建流水线、边缘网络与预览环境都与框架深度集成,大多数项目几乎零运维即可上线。
- 将代码推送到 GitHub(或 GitLab / Bitbucket)。
- 登录 vercel.com,选择 Import Project,授权仓库并选中你的 Next.js 仓库。
- Vercel 会自动识别 Next.js,使用默认的
next build与 Node 版本策略完成部署;每次 push 到默认分支会触发生产部署,其他分支可生成预览 URL。
环境变量:在项目的 Settings → Environment Variables 中添加键值,可按 Production / Preview / Development 区分。与本地一致地命名(例如 DATABASE_URL、NEXT_PUBLIC_SITE_URL),重新部署后生效。
自定义域名:在 Domains 中添加你的域名,按提示在 DNS 服务商处配置 A / CNAME 记录;Vercel 会自动申请并续期 HTTPS 证书。
💡 要点
Vercel 会自动检测 Next.js 项目结构并选用优化的构建与缓存策略,对常见场景接近零配置;把精力放在业务与 next.config 即可。
2. Docker 部署
当你需要自托管(私有云、Kubernetes、裸金属)时,Docker 是通用交付形态。Next.js 15 推荐使用 output: 'standalone',构建产物只包含运行所需的最小文件,镜像更小、启动更快。
next.config.ts 片段:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;
多阶段 Dockerfile 示例(Node 20,按项目路径微调 COPY):
# syntax=docker/dockerfile:1
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
WORKDIR /app
FROM base AS deps
COPY package.json package-lock.json* ./
RUN npm ci
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
FROM base AS runner
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
docker-compose.yml 示例(YAML,与 docker compose up 配合使用):
version: "3.8"
services:
web:
build: .
ports:
- "3000:3000"
environment:
NODE_ENV: production
构建与运行:
docker build -t my-next-app .
docker run --rm -p 3000:3000 my-next-app
# 或使用 compose
docker compose up --build
3. 静态导出
若站点完全是静态内容(文档、营销页),可使用静态导出,生成纯 HTML/CSS/JS,托管到任意静态服务器或 CDN。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
// 可选:无尾部斜杠等
// trailingSlash: true,
};
export default nextConfig;
限制(务必知晓):
- 无服务端运行时:不支持依赖 Node 服务器的 Route Handlers、Server Actions、中间件在边缘的行为需单独评估。
- 动态路由若未在构建期通过
generateStaticParams等方式枚举,则无法预渲染为独立 HTML(纯导出场景需事先确定所有路径)。 - 无增量 ISR、无按请求的服务端逻辑。
适用场景:博客静态镜像、产品说明站、离线可分发的前端包。构建产物在 out/,可推到 GitHub Pages、S3、Cloudflare Pages 等。
4. Metadata API(SEO)
App Router 使用 Metadata API 描述页面标题、描述、Open Graph、Twitter Card 等,由 Next 在服务端注入 <head>,利于 SEO 与社交分享。
静态 metadata(app/layout.tsx 或页面):
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
default: "我的站点",
template: "%s | 我的站点",
},
description: "基于 Next.js 15 的全栈站点",
openGraph: {
title: "我的站点",
description: "基于 Next.js 15 的全栈站点",
url: "https://example.com",
siteName: "我的站点",
locale: "zh_CN",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "我的站点",
description: "基于 Next.js 15 的全栈站点",
},
robots: { index: true, follow: true },
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-CN">
<body>{children}</body>
</html>
);
}
generateMetadata(动态页面,例如博客文章):
import type { Metadata } from "next";
type Props = { params: Promise<{ slug: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPostBySlug(slug); // 你的数据层
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: "article",
publishedTime: post.publishedAt,
},
};
}
export default async function Page({ params }: Props) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return <article><h1>{post.title}</h1></article>;
}
async function getPostBySlug(slug: string) {
return { title: "示例", excerpt: "摘要", publishedAt: new Date().toISOString() };
}
title.template:在 layout 中设置 template: '%s | 品牌名' 后,子页面只需 title: '文章标题' 即可得到完整标题。
Next.js 15 可在 app/robots.ts 与 app/sitemap.ts 中导出约定函数,自动生成 /robots.txt 与 /sitemap.xml:
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: { userAgent: "*", allow: "/", disallow: "/private/" },
sitemap: "https://example.com/sitemap.xml",
};
}
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{ url: "https://example.com", lastModified: new Date(), changeFrequency: "weekly", priority: 1 },
{ url: "https://example.com/blog", lastModified: new Date(), changeFrequency: "weekly", priority: 0.8 },
];
}
5. next.config.ts 常用配置
在根目录的 next.config.ts 中集中管理重定向、重写、安全头、图片域名与环境变量注入等。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
async redirects() {
return [{ source: "/old-blog/:slug", destination: "/blog/:slug", permanent: true }];
},
async rewrites() {
return [{ source: "/proxy-api/:path*", destination: "https://api.example.com/:path*" }];
},
async headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
],
},
];
},
images: {
remotePatterns: [{ protocol: "https", hostname: "cdn.example.com", pathname: "/images/**" }],
},
env: {
CUSTOM_BUILD_ID: process.env.VERCEL_GIT_COMMIT_SHA ?? "local",
},
};
export default nextConfig;
| 配置项 | 作用 |
|---|---|
| redirects | HTTP 重定向(301/302),适合改版换路径、合并路由。 |
| rewrites | URL 重写:浏览器地址不变,由 Next 反向代理到其它源。 |
| headers | 为匹配路径附加响应头(安全、缓存、CORS 等)。 |
| images.remotePatterns | 允许 next/image 优化的远程图片域名与路径模式。 |
| env | 将值内联到客户端 bundle(公开信息);敏感数据勿放此处。 |
6. 性能优化
Dynamic Import(next/dynamic)用于按需加载重型组件(图表、地图、富文本),减小首屏 JS。
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
loading: () => <p className="p-4">加载中…</p>,
ssr: false,
});
export default function Page() {
return (
<main>
<h1>仪表盘</h1>
<HeavyChart />
</main>
);
}
在纯客户端子树中也可使用 React.lazy + Suspense(需确保不破坏 RSC 边界规则,通常放在 Client Component 内):
"use client";
import { lazy, Suspense } from "react";
const Modal = lazy(() => import("./Modal"));
export function Panel() {
return (
<Suspense fallback={<span>…</span>}>
<Modal />
</Suspense>
);
}
Bundle Analyzer:安装 @next/bundle-analyzer,用环境变量开启分析:
npm install -D @next/bundle-analyzer
// next.config.ts
import type { NextConfig } from "next";
import bundleAnalyzer from "@next/bundle-analyzer";
const withAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === "true" });
const nextConfig: NextConfig = {};
export default withAnalyzer(nextConfig);
ANALYZE=true npm run build
图片:继续使用 next/image 自动 srcset、懒加载与尺寸占位。字体:使用 next/font(如 Inter、Noto_Sans_SC)自托管字体文件,减少布局偏移与外链请求。
有 React 经验时
性能优化仍是「测完再优化」:先用 Analyzer 找大包,再用 dynamic / lazy 拆包;图片与字体交给 Next 内置方案,通常比手写 <img> + 公共 CDN 字体更稳。
7. 常用环境变量
NEXT_PUBLIC_*:在浏览器端可访问,构建时内联进客户端 bundle。- 无前缀:仅在 Node 服务端可用(例如 Server Components、Route Handlers、Server Actions 内部)。
- 文件优先级(简化记忆):
.env→.env.local(本地覆盖,勿提交密钥)→.env.production/.env.development。
💡 要点
NEXT_PUBLIC_ 前缀的变量会进入客户端 JavaScript,任何用户都能在 DevTools 中看到,切勿存放 API 密钥、数据库连接串等敏感信息。
8. 本章要点与教程总结
部署路径
Vercel 与 Git 联动实现持续部署;自托管用 standalone + Docker;纯静态用 output: 'export' 并了解能力边界。
SEO
metadata / generateMetadata、OG/Twitter、robots.ts 与 sitemap.ts 形成完整搜索与分享元数据。
配置与性能
在 next.config 管理重定向、重写、头与图片域;用 dynamic / lazy 与 Bundle Analyzer 控制包体。
环境变量
区分 NEXT_PUBLIC_ 与服务器密钥;理解各 .env* 文件的用途与保密边界。