跳到内容

部署

恭喜,是时候发布到生产环境了。

您可以部署使用 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

Vercel,Next.js 的创建者和维护者,为您的 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 静态资源的 Web 服务器上。这包括 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 的逻辑(或使用外部包),您或许可以将此逻辑移动到 布局 作为 服务器组件。例如,检查 headersredirecting。您还可以使用 headers、cookies 或查询参数通过 next.config.js 重定向重写。如果这不起作用,您也可以使用自定义服务器

环境变量

Next.js 可以同时支持构建时和运行时环境变量。

默认情况下,环境变量仅在服务器上可用。要将环境变量公开给浏览器,它必须以 NEXT_PUBLIC_ 为前缀。但是,这些公共环境变量将在 next build 期间内联到 JavaScript 包中。

您可以在动态渲染期间安全地在服务器上读取环境变量。

app/page.ts
import { connection } from 'next/server'
 
export default async function Component() {
  await connection()
  // cookies, headers, and other Dynamic APIs
  // will also opt into dynamic rendering, meaning
  // this env variable is evaluated at runtime
  const value = process.env.MY_VALUE
  // ...
}

这允许您使用单个 Docker 镜像,该镜像可以通过具有不同值的多个环境进行升级。

须知

  • 您可以使用 register 函数在服务器启动时运行代码。
  • 我们不建议使用 runtimeConfig 选项,因为这不适用于独立输出模式。相反,我们建议逐步采用 App Router。

缓存和 ISR

Next.js 可以缓存响应、生成的静态页面、构建输出以及其他静态资源,如图像、字体和脚本。

缓存和重新验证页面(使用增量静态再生)使用相同的共享缓存。默认情况下,此缓存存储在 Next.js 服务器的文件系统(磁盘上)。当自托管时,无论使用 Pages Router 还是 App Router,这都会自动工作

如果您希望将缓存页面和数据持久化到持久存储,或者在 Next.js 应用程序的多个容器或实例之间共享缓存,您可以配置 Next.js 缓存位置。

自动缓存

  • Next.js 将 Cache-Control 标头设置为 public, max-age=31536000, immutable,以实现真正的不可变资源。它不能被覆盖。这些不可变文件在文件名中包含 SHA 哈希值,因此可以安全地无限期缓存。例如,静态图像导入。您可以配置图像的 TTL
  • 增量静态再生 (ISR) 将 Cache-Control 标头设置为 s-maxage: <revalidate in getStaticProps>, stale-while-revalidate。此重新验证时间在您的 getStaticProps 函数中以秒为单位定义。如果您设置 revalidate: false,则默认缓存持续时间为一年。
  • 动态渲染的页面将 Cache-Control 标头设置为 private, no-cache, no-store, max-age=0, must-revalidate,以防止缓存用户特定数据。这适用于 App Router 和 Pages Router。这也包括草稿模式

静态资源

如果您想在不同的域或 CDN 上托管静态资源,您可以使用 assetPrefix 配置next.config.js 中。当检索 JavaScript 或 CSS 文件时,Next.js 将使用此前缀。将您的资源分离到不同的域确实会带来在 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.includes(tag))) {
        cache.delete(key)
      }
    }
  }
 
  // If you want to have temporary in memory cache for a single request that is reset
  // before the next request you can leverage this method
  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 () => {
    // 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 标头。

流式处理和 Suspense

Next.js App Router 在自托管时支持流式响应。如果您正在使用 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 上缓存。

如果您不需要静态和动态组件的混合,您可以将整个路由设为静态,并将输出 HTML 缓存在 CDN 上。如果未使用动态 API,则此自动静态优化是在运行 next build 时的默认行为。

after

after 当使用 next start 进行自托管时,完全支持 after

停止服务器时,通过发送 SIGINTSIGTERM 信号并等待来确保优雅关闭。这允许 Next.js 服务器等待,直到在 after 中使用的待处理回调函数或 Promise 完成。

如果您想在自定义基础设施上使用 after,请查看您的提供商文档以了解对 after 的支持。

参考:为无服务器平台支持 after在无服务器环境中使用 after 需要在发送响应后等待异步任务完成。在 Next.js 和 Vercel 中,这是通过使用一个名为 waitUntil(promise) 的原语来实现的,该原语会延长无服务器调用的生命周期,直到传递给 waitUntil 的所有 Promise 都已解决。

如果您希望您的用户能够运行 after,您将必须提供您自己的 waitUntil 实现,该实现的行为方式类似。

当调用 after 时,Next.js 将像这样访问 waitUntil

const RequestContext = globalThis[Symbol.for('@next/request-context')]
const contextValue = RequestContext?.get()
const waitUntil = contextValue?.waitUntil

这意味着 globalThis[Symbol.for('@next/request-context')] 应该包含像这样的对象

type NextRequestContext = {
  get(): NextRequestContextValue | undefined
}
 
type NextRequestContextValue = {
  waitUntil?: (promise: Promise<any>) => void
}

这是一个实现的示例。

import { AsyncLocalStorage } from 'node:async_hooks'
 
const RequestContextStorage = new AsyncLocalStorage<NextRequestContextValue>()
 
// Define and inject the accessor that next.js will use
const RequestContext: NextRequestContext = {
  get() {
    return RequestContextStorage.getStore()
  },
}
globalThis[Symbol.for('@next/request-context')] = RequestContext
 
const handler = (req, res) => {
  const contextValue = { waitUntil: YOUR_WAITUNTIL }
  // Provide the value
  return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res))
}