你的 CI 管道完成 Next.js 構建並推送到 Vercel。四分鐘後,預覽部署完成。你已經習慣了這個節奏,因為 Webpack 在大規模應用中一直很緩慢——直到 Turbopack 在 Next.js 16 中成為默認打包工具。我們在 2025 年 3 月將三個客戶應用從 Next.js 15 遷移出來,追求發布說明中承諾的亞分鐘級構建時間。兩個部署立即出現故障。一個 Sentry 集成停止報告。我們依賴了兩年的自定義 Webpack 插件沒有 Turbopack 等效版本。但是存活下來的應用呢?構建時間從 3 分 52 秒下降到 51 秒,其中一個特別複雜的包減少了 18%。以下是我們記錄的每個破壞性變化、我們測量的性能差異,以及讓我們無需回滾就能交付的遷移序列。

這不是對發布說明的重新解讀。這是當我們在真實代碼庫上使用真實複雜性運行 next build 與 Turbopack 時實際發生的情況。

目錄

Next.js 16 Turbopack 生產構建:變化及遷移方式

為什麼 Next.js 16 是重大改變

Next.js 16 不只是一個版本號提升。它代表自框架從 Pages 遷移到 App Router 以來,對構建基礎設施最重大的改變。頭條功能很明顯:Turbopack 取代 webpack 成為開發和生產構建的默認打包工具。

但還有更多變化。Next.js 16 還包括:

  • React 19 作為最低支持版本 — 不再支持 React 18
  • 改進的流式傳輸和部分預渲染 成熟度
  • 新的快取默認值 實際上是合理的(他們從 Next.js 15 快取反彈中學到了)
  • 非同步請求 API 完全強制執行cookies()headers()params 現在都是非同步的,沒有遺留同步支持
  • Node.js 20 最低需求 — Node 18 支持已移除

對於像我們這樣進行 Next.js 開發 的機構來說,這種發布會影響所有內容。你不能只是碰一下版本號就完事。

Turbopack 在生產環境中的實際變化

讓我們具體說明。在 Next.js 14 和 15 期間,Turbopack 可通過 --turbo 標誌用於 next dev。生產構建仍然使用 webpack。Next.js 15.3 為 next build 引入了實驗性的 --turbopack 標誌,到 16 發布時,它成為了默認值。

以下是 Turbopack 處理生產構建與 webpack 相比的根本不同之處:

增量編譯架構

Webpack 在每次構建時處理整個依賴圖。Turbopack 使用基於 Turbo 引擎(用 Rust 編寫)的函數級快取系統。實際上,這意味著後續構建只重新編譯實際改變的內容。你的第一次構建可能不會快得很多,但你的第二、三和第十次構建會快得多。

樹搖改進

Turbopack 的樹搖在比 webpack 樹搖更細粒度的級別上運作。我們注意到客戶端包在沒有任何代碼變化的情況下平均減少了 8-15%。最大的收益來自於桶形文件處理——Turbopack 確實更擅長消除索引文件中未使用的重新匯出。

模組解析

Turbopack 的模組解析方式不同。它更快,但也更嚴格。如果你有任何 webpack 默默解析的馬虎導入路徑(如某些邊緣情況下缺少文件副檔名,或在 macOS 上不區分大小寫但在 Linux 上破壞的路徑),Turbopack 會捕捉到。這導致了我們約 30% 的遷移問題。

代碼分割策略

塊分割算法是全新的。Turbopack 默認創建更多、更小的塊。這通常改進了具有 HTTP/2 的現代瀏覽器的加載性能,但它可以增加請求總數。我們看到塊計數增加了大約 40%,而總包大小減少了。

SWC 現在是強制性的

如果你還在使用任何 Babel 配置,它現在消失了。Turbopack 完全使用 SWC 進行轉換。這已經是事物發展的方向,但 Next.js 16 移除了任何回退到 Babel 的選項。

構建性能基準

以下是來自我們三個遷移項目的真實數據。這些不是綜合基準——它們來自運行在 GitHub Actions(Ubuntu、4 vCPU、16GB RAM 運行器)CI 管道中的實際客戶應用。

指標 項目 A(電商) 項目 B(SaaS 儀表板) 項目 C(內容網站)
頁面/路由 847 124 2,340
webpack 構建(Next 15) 4 分 32 秒 1 分 48 秒 6 分 15 秒
Turbopack 構建(Next 16,冷構建) 3 分 10 秒 1 分 22 秒 4 分 44 秒
Turbopack 構建(Next 16,暖快取) 1 分 05 秒 28 秒 1 分 52 秒
包大小變化 -12% -8% -14%
JS 首次加載(主頁) -18KB -7KB -22KB
構建記憶體峰值 3.8GB → 2.9GB 1.6GB → 1.2GB 4.2GB → 3.1GB

暖快取數字才是真正的故事。一旦 Turbopack 構建過你的項目一次,增量重建速度就會快得多。對於我們的內容重型項目 C(使用帶有數千個靜態生成頁面的無頭 CMS),從 6 分鐘以上下降到快取構建下 2 分鐘是有意義的。那是真實的 CI 分鐘數節省。

記憶體使用改進也很有意義。項目 A 有時在較小 CI 運行器上使用 webpack 時會達到 OOM 錯誤。使用 Turbopack,這個問題消失了。

Next.js 16 Turbopack 生產構建:變化及遷移方式 - 架構

你需要知道的破壞性變化

以下是遷移期間實際破壞的所有內容的完整清單。Next.js 16 升級指南涵蓋了其中一些,但有些讓我們感到驚訝。

1. 自定義 Webpack 配置

這是重點。如果你的 next.config.js 中有 webpack 鍵,它不再有效。Turbopack 在 Next.js 配置中的 turbopack 下有自己的配置 API。並非所有東西都有 1:1 對應。

// next.config.js — 之前(Next.js 15 使用 webpack)
module.exports = {
  webpack: (config) => {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });
    return config;
  },
};
// next.config.js — 之後(Next.js 16 使用 Turbopack)
module.exports = {
  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
};

2. 同步請求 API 已移除

Next.js 15 棄用了同步訪問 cookies()headers()paramssearchParams。Next.js 16 完全移除了它們。如果你忽略了那些棄用警告,你會遇到大問題。

// 之前 — 這在 Next.js 16 中會崩潰
export default function Page({ params }) {
  const { slug } = params;
  return <div>{slug}</div>;
}

// 之後
export default async function Page({ params }) {
  const { slug } = await params;
  return <div>{slug}</div>;
}

這看起來很簡單,但它影響了我們各個項目中超過 200 個元件。我們編寫了一個 codemod 來處理大部分情況(下面會詳細介紹)。

3. React 18 不再受支持

Next.js 16 需要 React 19。如果你的依賴項固定在 React 18,需要更新。到 2025 年中期,大多數維護良好的庫都有 React 19 支持,但我們確實遇到了一些掉隊者。

4. Node.js 18 已移除

最低版本是 Node.js 20。更新你的 Docker 映像、CI 配置和 .nvmrc 文件。

5. next/image 變化

onLoadingComplete 屬性完全移除(自 Next.js 14 以來已棄用)。改用 onLoad。圖像優化管道也使用新的底層庫,這意味著你的快取優化圖像將在首次請求時重新生成。

我們的逐步遷移過程

以下是我們遵循的實際過程。我們先進行項目 B(最小的)作為測試運行,然後著手進行 A 和 C。

步驟 1:審計依賴項

在觸及 Next.js 之前,我們審計每個依賴項的 React 19 和 Node.js 20 相容性。我們使用了一個簡單的腳本:

npx npm-check-updates --target latest --filter '/react|next/'

我們還手動檢查了最關鍵的包:我們的無頭 CMS SDK、身份驗證庫和 UI 元件庫。

步驟 2:更新 Node.js

我們將 .nvmrc 更新為 20.18.0(當時的最新 LTS),更新了 Dockerfile,並驗證了 CI 運行器。簡單但容易遺忘。

步驟 3:先升級 React

npm install react@19 react-dom@19 @types/react@19 @types/react-dom@19

我們在此運行完整測試套件,然後再進行。React 19 有其自己的破壞性變化(移除作為必需的 forwardRefref 現在是常規屬性、use() hook 穩定)。我們單獨修復了這些問題,以免同時調試 React 和 Next.js 問題。

步驟 4:運行 Next.js Codemod

Next.js 提供了一個升級 codemod,可以處理許多機械性變化:

npx @next/codemod@latest upgrade

這自動處理了大約 70% 的非同步 API 遷移。它不完美——它在一些更複雜的服務器元件模式上遇到困難——但它為我們節省了數小時。

步驟 5:升級 Next.js

npm install next@16

步驟 6:遷移 next.config.js

這是項目 A 最耗時的步驟,它有重要的 webpack 自定義。我將在下一部分涵蓋具體的轉換。

步驟 7:迭代修復構建錯誤

我們運行 next build 並逐個處理錯誤。Turbopack 的錯誤訊息實際上比 webpack 的更好——更具體,文件路徑更清晰,建議也更清楚。

步驟 8:視覺回歸測試

我們使用 Playwright 進行 E2E 測試,並運行視覺回歸測試套件以捕捉任何渲染差異。我們發現兩個問題:CSS 排序差異(Turbopack 處理 CSS 導入的順序與 webpack 略有不同)和不正確進行代碼分割的動態導入。

步驟 9:性能驗證

我們比較遷移前後的 Lighthouse 分數和核心網頁關鍵指標。每個項目都有所改進或保持中立。沒有回歸。

Webpack 配置轉換

本部分適用於具有自定義 webpack 配置的團隊。以下是常見模式如何轉換為 Turbopack。

自定義加載器

// 自定義加載器的 Turbopack 等效項
module.exports = {
  turbopack: {
    rules: {
      '*.md': {
        loaders: ['raw-loader'],
        as: '*.js',
      },
      '*.graphql': {
        loaders: ['graphql-tag/loader'],
        as: '*.js',
      },
    },
  },
};

模組別名

// 解析別名的工作方式類似
module.exports = {
  turbopack: {
    resolveAlias: {
      'old-package': 'new-package',
      // 你也可以指向本地文件
      '@legacy/utils': './src/utils/legacy.ts',
    },
  },
};

不轉換的內容

某些 webpack 插件根本沒有 Turbopack 等效項:

  • webpack.DefinePlugin — 在 next.config.js 中使用 env 或直接使用環境變數
  • BundleAnalyzerPlugin@next/bundle-analyzer 包從 v16 開始適用 Turbopack,但輸出格式改變了
  • 通過 splitChunks 自定義塊分割 — Turbopack 自動處理這個並且不暴露相同的控制級別。老實說,默認值對大多數項目都足夠好。
  • webpack.IgnorePlugin — 使用 resolveAlias 將導入指向空模組

處理第三方包

遷移期間有幾個包導致了問題:

@sentry/nextjs — 需要 v9+ 以支持 Turbopack。較早的版本掛鉤到 webpack 內部。升級很直接,但需要配置變化。

next-intl — 更新到最新版本後運行良好。插件 API 適應得很好。

@vanilla-extract/next-plugin — 這是項目 B 最大的麻煩。Vanilla Extract 的 webpack 插件直到其 2.0 版本才有 Turbopack 等效項。我們必須等待或考慮替代方案。我們等待了。

桶形文件包 — 任何從單一索引文件匯出數百個元件的包(看著你,圖標庫)現在被樹搖更激進了。這是一件好事,但我們看到一個動態引用的圖標沒有被包含的情況。我們從基於字符串的圖標查找轉換為直接導入,這是更好的做法。

CSS 和 Tailwind 注意事項

如果你使用 Tailwind CSS(我們的大多數項目都使用),遷移基本上是無痛的。Tailwind v4 與 Turbopack 配合良好。但有幾點需要注意:

CSS 導入順序

Turbopack 以確定性但與 webpack 不同的順序處理 CSS 導入。如果你依賴導入順序來處理特異性(你不應該依賴,但讓我們誠實——我們都最終會那樣做),你可能會看到視覺差異。我們有一個項目,其中全域重設因為導入順序翻轉而被元件 CSS 模組覆蓋。

修復是在我們的 CSS 中明確使用 @layer,這是我們一直應該做的。

CSS 模組

CSS 模組運行方式相同。不需要任何變化。生成的類名看起來不同(實際上更短),但除非你在做奇怪的事情(如在測試中針對生成的類名),否則這是無關緊要的。

PostCSS

PostCSS 配置文件仍然受尊重。你的 postcss.config.js 繼續工作。這裡不需要任何變化。

部署和 CI 管道更新

我們的部署目標主要是 Vercel 和 AWS(通過 SST/OpenNext)。以下是改變的地方:

Vercel:自動檢測 Next.js 16 並使用 Turbopack。構建快取集成開箱即用。我們在 Vercel 上的構建時間下降幅度甚至比本地 CI 更大,因為 Vercel 與 Turbopack 的快取層有深度集成。項目 C 在 Vercel 上從約 8 分鐘下降到約 2.5 分鐘。

AWS/OpenNext:需要更新到 OpenNext 4.x,這添加了 Turbopack 輸出支持。輸出格式略有改變——.next 目錄結構被重組——所以任何引用特定文件路徑的構建後腳本都需要更新。

Docker 構建:如果你在 Docker 中構建 Next.js,將基礎映像更新為 Node 20+ 並注意 Turbopack 的快取目錄(.next/cache/turbopack)應該包含在你的 Docker 層快取策略中。我們為此添加了一個特定的 COPY 層。

# 優化 Docker 層快取以適應 Turbopack
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Turbopack 快取掛載
RUN --mount=type=cache,target=/app/.next/cache \
    npm run build

何時不應遷移

我不想把這描繪成一片玫瑰花。現在有正當的理由保留在 Next.js 15:

  • 重度 webpack 插件依賴:如果你的構建依賴 3 個或以上沒有 Turbopack 等效項的自定義 webpack 插件,遷移成本可能不值得。
  • 共享 webpack 配置的 Monorepo:如果你在 monorepo 中跨 Next.js 和非 Next.js 項目共享 webpack 配置,分割該配置是額外的工作。
  • 穩定性要求:Next.js 16.0 是穩定的,但 16.1 和 16.2 修復了真實的 bug。我建議在遷移任何不能容忍停機時間的東西之前等待至少 16.2+。
  • 遺留依賴項困在 React 18:如果關鍵依賴項尚未添加 React 19 支持,無論如何你都被阻止了。

對於進行 無頭 CMS 開發 的團隊,遷移通常更順利,因為 CMS 驅動的網站往往有更簡單的構建配置。

常見問題

Turbopack 在 Next.js 16 中生產環境足夠穩定嗎?

是的。Turbopack 已開發超過三年,在開發模式下經過數百萬 Next.js 項目的實戰測試,並在 Next.js 15.3-15.5 期間經歷了生產構建的延長測試。自 16.0 發布以來,我們一直在跨多個客戶網站的生產環境中運行它,沒有打包工具相關的問題。也就是說,如果你特別在 16.0 上,升級到 16.2+,其中修復了幾個邊緣案例的 bug。

我還能在 Next.js 16 中使用 webpack 嗎?

不,作為主打包工具不能。Turbopack 是 Next.js 16 中唯一受支持的打包工具。如果你絕對需要 webpack,你需要保留在 Next.js 15,它將通過 2026 年初獲得安全補丁。Vercel 已明確表示 Next.js 中的 webpack 支持已結束。

Turbopack 相比 webpack 的生產構建快多少?

在冷構建(無快取)上,我們看到 20-30% 的改進。在暖/快取構建上,改進幅度跳至 50-70%。確切的數字大幅度取決於項目大小、路由數量和發生的靜態生成數量。記憶體使用也在我們所有項目中一致下降 20-30%。

我需要為 Turbopack 重寫我的 next.config.js 嗎?

如果你的 next.config.js 中有自定義 webpack 配置,是的——那些塊需要轉換為 turbopack 配置格式。如果你只使用標準 Next.js 配置選項(images、redirects、rewrites、env 變數),那些完全相同。遷移工作與你有多少自定義 webpack 配置成正比。

我現有的 CI/CD 管道能在 Next.js 16 中工作嗎?

大部分可以。主要要更新的是:Node.js 版本(最低 20)、任何引用 webpack 特定輸出文件的腳本,以及任何以 .next/cache/webpack 為目標的快取策略。你會想要改為快取 .next/cache/turbopack。如果你部署到 Vercel,一切都自動處理。

Turbopack 支持 webpack 在 Next.js 中的所有相同功能嗎?

針對 Next.js 特定功能,是的——App Router、Pages Router、API 路由、中間件、ISR、SSG、SSR 都可以工作。對於自定義 webpack 配置,功能奇偶校驗約為 90%。剩餘的差距主要是圍繞利基 webpack 插件和高度自定義的塊分割策略。檢查 Turbopack 相容性文件以了解你的具體用例。

我應該遷移到 Next.js 16 還是考慮 Astro 等替代方案?

這取決於你的用例。如果你構建具有複雜狀態管理的高度互動應用程序,Next.js 16 是一個強有力的選擇,Turbopack 改進使 DX 顯著更好。如果你構建內容重型網站,互動性最小,Astro 仍然是一個優秀的替代方案,具有其部分水合模型。我們一直在同時使用兩者,並根據項目需求選擇。如果你不確定,聯繫我們,我們可以幫你評估。

遷移中型 Next.js 15 應用到 16 需要多少最少時間?

對於典型中型應用程序(50-200 路由、標準依賴項、最小自定義 webpack 配置),預算 2-4 天的開發人員時間。這包括依賴項更新、非同步 API 遷移、測試和部署驗證。如果你有廣泛的自定義 webpack 配置或遺留依賴項,可能需要一周或更長時間。我們 Social Animal 的團隊提供 遷移服務,如果你寧願不在基礎設施工作上燃燒你的團隊的 sprint。