Next.js静态导出深度解析:如何构建200+页面的高性能工具站

技术架构(更新于 2026年5月17日)

静态导出的核心原理

Next.js 的 output: "export" 模式将整个应用预渲染为纯静态 HTML,无需 Node.js 运行时:

next build (SSG)
     ↓
遍历所有 generateStaticParams()
     ↓
为每个 [locale] × [slug] 组合预渲染 HTML
     ↓
输出到 out/ 目录
     ↓
上传到 OSS/CDN

SSG vs SSR vs ISR

模式 需要服务器 构建时渲染 更新方式 适用场景
SSG (export) 不需要 重新构建部署 内容固定的工具站
SSR 需要 每次请求渲染 个性化内容
ISR 需要 后台增量重渲染 频繁更新的内容

工具库选择 SSG 的理由:

  • 200+ 工具页内容完全静态,无需动态数据
  • 部署到阿里云 OSS + CDN,零运维成本
  • 首屏 TTFB 极低(CDN 直接返回 HTML)
  • 天然支持离线缓存

generateStaticParams 的设计模式

多语言 × 工具页的笛卡尔积

// src/app/[locale]/layout.tsx
export function generateStaticParams() {
  return [
    { locale: 'zh-CN' },
    { locale: 'zh-TW' },
    { locale: 'en' },
    { locale: 'ja' },
  ];
}
// src/app/[locale]/pdf/merge/page.tsx
export function generateStaticParams() {
  return [
    { locale: 'zh-CN' },
    { locale: 'zh-TW' },
    { locale: 'en' },
    { locale: 'ja' },
  ];
}

Next.js 会自动计算 layout.generateStaticParams × page.generateStaticParams笛卡尔积,为每个 (locale, path) 组合生成 HTML。

动态路由:博客文章的枚举

// src/app/[locale]/blog/[slug]/page.tsx
export function generateStaticParams() {
  return getStaticParamsForAllPosts().map(({ slug, locale }) => ({
    locale,
    slug,
  }));
}

getStaticParamsForAllPosts() 扫描 src/content/blog/ 目录,枚举所有 slug × 可用语言,确保每篇文章的每个语言版本都被预渲染。


构建性能优化

工具库的构建挑战

  • 292 个工具页 × 4 语言 = 1168 个 HTML 页面
  • 加上博客、分类页等,总计 1300+ 静态页面
  • 每个页面需要 getTranslations() 加载 i18n 消息

并发控制

// next.config.ts
experimental: {
  staticGenerationRetryCount: 2,
  ...(isCi
    ? {
        staticGenerationMaxConcurrency: 4,     // CI: 4 并发
        staticGenerationMinPagesPerWorker: 32,  // 每 worker 32 页
      }
    : {
        staticGenerationMaxConcurrency: 16,     // 本机: 16 并发
        staticGenerationMinPagesPerWorker: 8,
      }),
}
  • CI 环境:GitHub Actions 内存有限,压低并发避免 OOM
  • 本机开发:拉高并发加速构建

超时设置

staticPageGenerationTimeout: 180, // 3 分钟

部分工具页(如视频工具)加载 WASM 依赖较多,预渲染耗时可能超过默认 60s。


多语言路由架构

next-intl + App Router 集成

src/i18n/routing-config.ts
  → locales: ['zh-CN', 'zh-TW', 'en', 'ja']
  → localePrefix: 'always'  ← 所有 URL 带语言前缀
  → localeDetection: true   ← Accept-Language 自动协商

URL 结构

https://www.toolsku.com/zh-CN/pdf/merge/     ← 简体中文
https://www.toolsku.com/en/pdf/merge/         ← 英文
https://www.toolsku.com/zh-CN/blog/pdf-merge-guide/  ← 博客

hreflang 实现

每个页面都注入完整的 hreflang 标签:

<link rel="alternate" hreflang="zh-CN" href="https://www.toolsku.com/zh-CN/pdf/merge/" />
<link rel="alternate" hreflang="zh-TW" href="https://www.toolsku.com/zh-TW/pdf/merge/" />
<link rel="alternate" hreflang="en" href="https://www.toolsku.com/en/pdf/merge/" />
<link rel="alternate" hreflang="ja" href="https://www.toolsku.com/ja/pdf/merge/" />
<link rel="alternate" hreflang="x-default" href="https://www.toolsku.com/zh-CN/pdf/merge/" />

Sitemap 自动发现

工具库的 sitemap.ts 使用文件系统扫描自动发现所有页面:

const pathEntries = discoverLocaleInnerPathsWithFilePath(appDir);
// 自动发现 src/app/[locale]/ 下的所有 page.tsx

跳过动态路由

discoverLocaleInnerPaths 设计上跳过动态段(如 [slug]),因为它们需要参数才能渲染。

对于博客文章,sitemap.ts 额外处理

const blogSlugs = getAllBlogSlugs();
for (const slug of blogSlugs) {
  entries.push({
    url: absoluteLocalizedUrl(locale, `/blog/${slug}`),
    lastModified: meta.updatedAt,
    priority: 0.7,
    alternates: { languages },
  });
}

CDN 部署架构

资源前缀 (Asset Prefix)

// next.config.ts
assetPrefix: getAssetPrefix(),
// → 生产环境指向 https://toolsku.oss-cn-beijing.aliyuncs.com/public

构建产物中 _next/static/ 下的 JS/CSS 文件都指向 CDN,HTML 由 OSS 直接服务。

部署流程

next build → out/
     ↓
ossutil cp out/ oss://toolsku/ --recursive
     ↓
CDN 自动刷新(或等待 TTL)
     ↓
新版本上线

缓存策略

资源类型 缓存策略 理由
_next/static/**/*.js Cache-Control: max-age=31536000, immutable 文件名含 hash
*.html Cache-Control: max-age=3600, s-maxage=86400 页面可能更新
*.wasm Cache-Control: max-age=31536000 WASM 版本稳定
i18n/*.json Cache-Control: max-age=86400 搜索索引定期更新

构建流水线

yarn build
  → generate-tools-search-index.mjs    ← 生成工具搜索索引
  → generate-tool-clients-public.mjs   ← 生成工具客户端数据
  → next build                          ← SSG 预渲染所有页面
     → 遍历 generateStaticParams()
     → 渲染每个 (locale, path) 组合
     → 输出到 out/

增量构建的挑战

Next.js 静态导出目前不支持增量构建——每次 next build 都会重新渲染所有页面。对于 1300+ 页面的项目,完整构建需要 3-5 分钟。

应对策略

  • CI 环境压低并发减少内存峰值
  • 本机开发使用 dev 模式(按需渲染,不预生成)
  • Turbopack 加速开发模式

经验总结

决策 选择 理由
渲染模式 SSG (export) 零运维、极低 TTFB
路由模式 App Router Server Components、更好的代码分割
国际化 next-intl App Router 原生支持、完善的消息系统
内容管理 代码仓库 (JSON/TS) 无 CMS 依赖、Git 版本控制
部署 OSS + CDN 国内访问快、零运维

纯静态导出是工具类网站的最佳选择——内容固定、访问频繁、需要极致的首屏速度。Next.js App Router + SSG 的组合,让工具库实现了零服务器运维下的高性能部署。

本站提供浏览器本地工具,免注册即可试用 →

#Next.js#SSG#静态导出#CDN#性能优化