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#性能优化