跳至內容

部署

恭喜,是時候發佈到正式環境了。

您可以部署使用 Vercel 管理的 Next.js,或自行託管在 Node.js 伺服器、Docker 映像檔,甚至是靜態 HTML 檔案上。使用 next start 部署時,所有 Next.js 功能都受到支援。

正式版建置

執行 next build 會產生應用程式的最佳化版本以供正式環境使用。HTML、CSS 和 JavaScript 檔案會根據您的頁面建立。JavaScript 會使用 Next.js 編譯器 進行編譯,瀏覽器套件會進行最小化,以協助達到最佳效能並支援所有現代瀏覽器

Next.js 會產生一個標準部署輸出,適用於 Next.js 的託管和自行託管部署。這確保所有功能在這兩種部署方法中都受到支援。在下一個主要版本中,我們會將此輸出轉換為我們的 建置輸出 API 規範

使用 Vercel 託管 Next.js

Next.js 的建立者和維護者 Vercel,為您的 Next.js 應用程式提供託管式基礎架構和開發者體驗平台。

部署到 Vercel 是零設定的,並提供額外的增強功能,以實現全球範圍的可擴展性、可用性和效能。然而,所有 Next.js 功能在自行託管時仍然受到支援。

深入了解 Vercel 上的 Next.js免費部署範本 來試用看看。

自行託管

您可以透過三種不同的方式自行託管 Next.js

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

Node.js 伺服器

Next.js 可以部署到任何支援 Node.js 的託管服務供應商。請確保您的 package.json 檔案中有 "build""start" 指令碼

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

接著,執行 npm run build 來建置您的應用程式。最後,執行 npm run start 來啟動 Node.js 伺服器。此伺服器支援所有 Next.js 功能。

Docker 映像檔

Next.js 可以部署到任何支援 Docker 容器的託管服務供應商。當您部署到容器協調器(例如 Kubernetes)或在任何雲端供應商的容器內執行時,可以使用這種方法。

  1. 在您的機器上安裝 Docker
  2. 複製我們的範例(或 多環境範例
  3. 建置您的容器:docker build -t nextjs-docker .
  4. 執行您的容器:docker run -p 3000:3000 nextjs-docker

透過 Docker 執行的 Next.js 支援所有 Next.js 功能。

靜態 HTML 匯出

Next.js 可以先以靜態網站或單頁應用程式 (SPA) 的形式啟動,之後再選擇性地升級以使用需要伺服器的功能。

由於 Next.js 支援這種 靜態匯出 方式,它可以在任何可以提供 HTML/CSS/JS 靜態資源的網路伺服器上部署和託管。這包括 AWS S3、Nginx 或 Apache 等工具。

靜態匯出 方式運行不支援需要伺服器的 Next.js 功能。了解更多

注意事項

功能

圖片優化

使用 next start 部署時,透過 next/image 進行的 圖片優化 可在自行託管的環境下以零設定運作。如果您希望使用個別的服務來優化圖片,您可以設定圖片載入器

透過在 next.config.js 中定義自訂圖片載入器,圖片優化功能可以與 靜態匯出 一起使用。請注意,圖片是在運行時進行優化的,而不是在建置期間。

注意事項

  • 在基於 glibc 的 Linux 系統上,圖片優化可能需要 額外設定 以防止過度使用記憶體。
  • 深入瞭解 已優化圖片的快取行為 以及如何設定 TTL。
  • 如果您願意,也可以 停用圖片優化,並仍然保留使用 next/image 的其他優點。例如,如果您是自己另外優化圖片。

中介軟體

使用 next start 部署時,中介軟體 可在自行託管的環境下以零設定運作。由於它需要存取傳入請求,因此在使用 靜態匯出 時不受支援。

中介軟體使用所有可用 Node.js API 的子集作為 運行環境,以協助確保低延遲,因為它可能在應用程式中的每個路由或資源之前運行。此運行環境不需要在「邊緣」運行,並且可以在單一區域的伺服器中運作。要在多個區域運行中介軟體,需要額外的設定和基礎架構。

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

環境變數

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

預設情況下,環境變數僅在伺服器上可用。要將環境變數公開給瀏覽器,它必須以 NEXT_PUBLIC_ 作為前綴。然而,這些公開的環境變數將在 next build 期間被內嵌到 JavaScript 捆綁包中。

要讀取執行階段環境變數,我們建議使用 getServerSideProps逐步採用 App Router

這允許您使用單個 Docker 映像,並透過具有不同值的多個環境進行升級。

注意事項

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

快取和 ISR

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

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

如果您想要將快取的頁面和資料保存到持久儲存空間,或在多個容器或 Next.js 應用程式的執行個體之間共享快取,則可以設定 Next.js 快取位置。

自動快取 靜態圖片匯入。您可以設定圖片的 TTL
  • 增量靜態再生 (ISR) 會將 Cache-Control 標頭設定為 s-maxage: <getStaticProps 中的 revalidate 值>, 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, // disable default in-memory caching
    }

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

    cache-handler.js
    const cache = new Map()
     
    module.exports = class CacheHandler {
      constructor(options) {
        this.options = options
      }
     
      async get(key) {
        // This could be stored anywhere, like durable storage
        return cache.get(key)
      }
     
      async set(key, data, ctx) {
        // This could be stored anywhere, like durable storage
        cache.set(key, {
          value: data,
          lastModified: Date.now(),
          tags: ctx.tags,
        })
      }
     
      async revalidateTag(tags) {
        // tags is either a string or an array of strings
        tags = [tags].flat()
        // Iterate over all entries in the cache
        for (let [key, value] of cache) {
          // If the value's tags include the specified tag, delete this entry
          if (value.tags.some((tag) => tags.include(tag))) {
            cache.delete(key)
          }
        }
      }
    }

    使用自訂快取處理器能確保所有裝載 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 () => {
        // This could be anything, using the latest git hash
        return process.env.GIT_HASH
      },
    }

    版本偏差

    Next.js 會自動減輕大多數 版本偏差 的情況,並在偵測到版本偏差時自動重新載入應用程式以取得新的資源。例如,如果 deploymentId 不符,頁面之間的轉換將會執行硬性導航,而不是使用預先擷取的值。

    當應用程式重新載入時,如果應用程式未設計為在頁面導航之間保持狀態,則可能會遺失應用程式狀態。例如,使用 URL 狀態或本地儲存空間會在頁面重新整理後保留狀態。但是,像 useState 這樣的組件狀態將會在此類導航中遺失。

    Vercel 為 Next.js 應用程式提供額外的 偏差保護,以確保即使在新版本部署後,舊版用戶端仍然可以使用先前版本的資源和函式。

    您可以在 next.config.js 檔案中手動設定 deploymentId 屬性,以確保每個請求都使用 ?dpl 查詢字串或 x-deployment-id 標頭。

    手動優雅關閉

    在自行託管時,您可能希望在伺服器因 SIGTERMSIGINT 訊號關閉時執行程式碼。

    您可以將環境變數 NEXT_MANUAL_SIG_HANDLE 設定為 true,然後在 _document.js 檔案中註冊該訊號的處理程式。您需要直接在 package.json 腳本中註冊環境變數,而不是在 .env 檔案中。

    注意事項next dev 不支援手動訊號處理。

    package.json
    {
      "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "NEXT_MANUAL_SIG_HANDLE=true next start"
      }
    }
    pages/_document.js
    if (process.env.NEXT_MANUAL_SIG_HANDLE) {
      process.on('SIGTERM', () => {
        console.log('Received SIGTERM: cleaning up')
        process.exit(0)
      })
      process.on('SIGINT', () => {
        console.log('Received SIGINT: cleaning up')
        process.exit(0)
      })
    }