← 返回目录

第八章:部署与优化

Vercel/Docker 部署、SEO 优化与性能调优

1. Vercel 部署

Vercel 由 Next.js 团队打造,是与 Next.js 契合度最高的托管平台:构建流水线、边缘网络与预览环境都与框架深度集成,大多数项目几乎零运维即可上线。

环境变量:在项目的 Settings → Environment Variables 中添加键值,可按 Production / Preview / Development 区分。与本地一致地命名(例如 DATABASE_URLNEXT_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;

限制(务必知晓)

适用场景:博客静态镜像、产品说明站、离线可分发的前端包。构建产物在 out/,可推到 GitHub Pages、S3、Cloudflare Pages 等。

4. Metadata API(SEO)

App Router 使用 Metadata API 描述页面标题、描述、Open Graph、Twitter Card 等,由 Next 在服务端注入 <head>,利于 SEO 与社交分享。

静态 metadataapp/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.tsapp/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 Importnext/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(如 InterNoto_Sans_SC)自托管字体文件,减少布局偏移与外链请求。

有 React 经验时

性能优化仍是「测完再优化」:先用 Analyzer 找大包,再用 dynamic / lazy 拆包;图片与字体交给 Next 内置方案,通常比手写 <img> + 公共 CDN 字体更稳。

7. 常用环境变量

💡 要点

NEXT_PUBLIC_ 前缀的变量会进入客户端 JavaScript,任何用户都能在 DevTools 中看到,切勿存放 API 密钥、数据库连接串等敏感信息。

8. 本章要点与教程总结

部署路径

Vercel 与 Git 联动实现持续部署;自托管用 standalone + Docker;纯静态用 output: 'export' 并了解能力边界。

SEO

metadata / generateMetadata、OG/Twitter、robots.tssitemap.ts 形成完整搜索与分享元数据。

配置与性能

next.config 管理重定向、重写、头与图片域;用 dynamic / lazy 与 Bundle Analyzer 控制包体。

环境变量

区分 NEXT_PUBLIC_ 与服务器密钥;理解各 .env* 文件的用途与保密边界。

🎉 恭喜完成

你已走完本速成教程的核心章节:从搭建、路由、渲染、数据、样式、状态到数据库与部署优化。

推荐下一步