如何自行託管 Next.js 應用程式

部署您的 Next.js 應用程式時,您可能希望根據基礎架構配置不同功能的處理方式。

🎥 觀看影片: 深入了解自行託管 Next.js → YouTube (45 分鐘)

圖片最佳化

透過 next/image 進行的圖片最佳化在自行託管時使用 next start 部署時無需配置即可工作。如果您希望使用單獨的服務來最佳化圖片,可以配置圖片載入器

圖片最佳化可以與靜態匯出一起使用,方法是在 next.config.js 中定義自訂圖片載入器。請注意,圖片是在執行時最佳化,而非建置期間。

須知事項:

中介軟體

中介軟體在自行託管時使用 next start 部署時無需配置即可工作。由於它需要存取傳入請求,因此在使用靜態匯出時不受支援。

中介軟體使用邊緣運行時,這是所有可用 Node.js API 的子集,以幫助確保低延遲,因為它可能在應用程式的每個路由或資源之前運行。如果您不希望這樣,可以使用完整的 Node.js 運行時來運行中介軟體。

如果您希望新增需要所有 Node.js API 的邏輯(或使用外部套件),您可以將此邏輯移至佈局作為伺服器元件。例如,檢查標頭重新導向。您也可以使用標頭、Cookie 或查詢參數透過 next.config.js 進行重新導向重寫。如果這不起作用,您也可以使用自訂伺服器

環境變數

Next.js 支援建置時和執行時環境變數。

預設情況下,環境變數僅在伺服器上可用。要將環境變數公開給瀏覽器,必須加上 NEXT_PUBLIC_ 前綴。然而,這些公開環境變數將在 next build 期間內嵌到 JavaScript 套件中。

您可以在動態渲染期間安全地在伺服器上讀取環境變數。

import { connection } from 'next/server'

export default async function Component() {
  await connection()
  // cookies、headers 和其他動態 API
  // 也會選擇動態渲染,意味著
  // 此環境變數在執行時評估
  const value = process.env.MY_VALUE
  // ...
}
import { connection } from 'next/server'

export default async function Component() {
  await connection()
  // cookies、headers 和其他動態 API
  // 也會選擇動態渲染,意味著
  // 此環境變數在執行時評估
  const value = process.env.MY_VALUE
  // ...
}

這允許您使用單一的 Docker 映像,可以在多個環境中推廣,並使用不同的值。

須知事項:

  • 您可以使用 register 函式在伺服器啟動時執行程式碼。
  • 我們不建議使用 runtimeConfig 選項,因為這不適用於獨立輸出模式。相反,我們建議逐步採用應用程式路由器。

快取與 ISR

Next.js 可以快取回應、生成的靜態頁面、建置輸出以及其他靜態資源,如圖片、字型和腳本。

快取和重新驗證頁面(使用增量靜態再生)使用相同的共享快取。預設情況下,此快取儲存在 Next.js 伺服器的檔案系統(磁碟)上。這在自行託管時自動工作,適用於頁面和應用程式路由器。

如果您希望將快取的頁面和資料持久化到持久儲存,或在多個容器或 Next.js 應用程式實例之間共享快取,可以配置 Next.js 快取位置。

自動快取

  • Next.js 為真正不可變的資源設定 Cache-Control 標頭為 public, max-age=31536000, immutable。這無法覆蓋。這些不可變檔案在檔案名稱中包含 SHA 雜湊,因此可以安全地永久快取。例如,靜態圖片匯入。您可以為圖片配置 TTL
  • 增量靜態再生 (ISR) 設定 Cache-Control 標頭為 s-maxage: <在 getStaticProps 中重新驗證>, stale-while-revalidate。此重新驗證時間在您的 getStaticProps 函式中以秒為單位定義。如果您設定 revalidate: false,它將預設為一年的快取持續時間。
  • 動態渲染的頁面設定 Cache-Control 標頭為 private, no-cache, no-store, max-age=0, must-revalidate 以防止使用者特定資料被快取。這適用於應用程式路由器和頁面路由器。這也包括草稿模式

靜態資源

如果您希望在不同的網域或 CDN 上託管靜態資源,可以在 next.config.js 中使用 assetPrefix 配置。Next.js 將在檢索 JavaScript 或 CSS 檔案時使用此資源前綴。將資源分離到不同的網域會帶來 DNS 和 TLS 解析的額外時間開銷。

深入了解 assetPrefix

配置快取

預設情況下,生成的快取資源將儲存在記憶體中(預設為 50mb)和磁碟上。如果您使用容器編排平台(如 Kubernetes)託管 Next.js,每個 pod 將擁有快取的副本。為防止顯示過時資料(因為預設情況下快取不在 pod 之間共享),您可以配置 Next.js 快取以提供快取處理程式並停用記憶體快取。

要在自行託管時配置 ISR/資料快取位置,可以在 next.config.js 檔案中配置自訂處理程式:

next.config.js
module.exports = {
  cacheHandler: require.resolve('./cache-handler.js'),
  cacheMaxMemorySize: 0, // 停用預設記憶體快取
}

然後,在專案根目錄中建立 cache-handler.js,例如:

cache-handler.js
const cache = new Map()

module.exports = class CacheHandler {
  constructor(options) {
    this.options = options
  }

  async get(key) {
    // 這可以儲存在任何地方,例如持久儲存
    return cache.get(key)
  }

  async set(key, data, ctx) {
    // 這可以儲存在任何地方,例如持久儲存
    cache.set(key, {
      value: data,
      lastModified: Date.now(),
      tags: ctx.tags,
    })
  }

  async revalidateTag(tags) {
    // tags 是字串或字串陣列
    tags = [tags].flat()
    // 遍歷快取中的所有條目
    for (let [key, value] of cache) {
      // 如果值的標籤包含指定的標籤,刪除此條目
      if (value.tags.some((tag) => tags.includes(tag))) {
        cache.delete(key)
      }
    }
  }

  // 如果您希望為單一請求擁有臨時記憶體快取,並在下一個請求之前重置
  // 您可以利用此方法
  resetRequestCache() {}
}

使用自訂快取處理程式可以確保所有託管 Next.js 應用程式的 pod 之間的一致性。例如,您可以將快取值儲存在任何地方,如 Redis 或 AWS S3。

須知事項:

  • revalidatePath 是快取標籤上的便利層。呼叫 revalidatePath 將使用提供的頁面的特殊預設標籤呼叫 revalidateTag 函式。

建置快取

Next.js 在 next build 期間生成一個 ID 來識別正在提供的應用程式版本。相同的建置應在多個容器中使用和啟動。

如果您為環境的每個階段重新建置,則需要生成一致的建置 ID 以在容器之間使用。在 next.config.js 中使用 generateBuildId 命令:

next.config.js
module.exports = {
  generateBuildId: async () => {
    // 這可以是任何內容,使用最新的 git 雜湊
    return process.env.GIT_HASH
  },
}

版本偏差

Next.js 將自動緩解大多數版本偏差實例,並在檢測到時自動重新載入應用程式以檢索新資源。例如,如果 deploymentId 不匹配,頁面之間的轉換將執行硬導航而非使用預取值。

當應用程式重新載入時,如果未設計為在頁面導航之間持久化,則可能會丟失應用程式狀態。例如,使用 URL 狀態或本地儲存將在頁面重新整理後持久化狀態。然而,元件狀態如 useState 將在此類導航中丟失。

串流與 Suspense

Next.js 應用程式路由器在自行託管時支援串流回應。如果您使用 Nginx 或類似代理,則需要配置它以停用緩衝以啟用串流。

例如,您可以透過將 X-Accel-Buffering 設定為 no 來在 Nginx 中停用緩衝:

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*{/}?',
        headers: [
          {
            key: 'X-Accel-Buffering',
            value: 'no',
          },
        ],
      },
    ]
  },
}

部分預渲染

部分預渲染(實驗性)預設與 Next.js 一起工作,並非僅限於 CDN 的功能。這包括作為 Node.js 伺服器(透過 next start)部署以及與 Docker 容器一起使用時。

與 CDN 一起使用

當在 Next.js 應用程式前使用 CDN 時,頁面將在存取動態 API 時包含 Cache-Control: private 回應標頭。這確保生成的 HTML 頁面標記為不可快取。如果頁面完全預渲染為靜態,它將包含 Cache-Control: public 以允許頁面在 CDN 上快取。

如果您不需要混合靜態和動態元件,可以使整個路由靜態並在 CDN 上快取輸出 HTML。如果未使用動態 API,則此自動靜態最佳化是執行 next build 時的預設行為。

隨著部分預渲染趨於穩定,我們將透過部署適配器 API 提供支援。

after

after 在使用 next start 自行託管時完全支援。

停止伺服器時,請確保透過發送 SIGINTSIGTERM 信號並等待來實現優雅關機。這允許 Next.js 伺服器等待,直到 after 內使用的待處理回呼函式或 Promise 完成。