如何自托管你的 Next.js 应用
当你部署 Next.js 应用时,可能需要根据基础设施配置不同功能的处理方式。
🎥 观看视频: 了解更多关于自托管 Next.js 的内容 → YouTube (45分钟)。
图片优化
通过 next/image
实现的图片优化在使用 next start
部署时可零配置自托管。如果希望使用单独的服务优化图片,可以配置图片加载器。
通过静态导出 (static export) 使用图片优化时,需要在 next.config.js
中定义自定义图片加载器。请注意,图片是在运行时优化,而非构建时。
须知:
中间件
中间件在使用 next start
部署时可零配置自托管。由于它需要访问传入请求,因此不支持静态导出。
中间件使用 Edge 运行时,这是所有可用 Node.js API 的子集,有助于确保低延迟,因为它可能在应用中的每个路由或资源前运行。如果不希望这样,可以使用完整的 Node.js 运行时来运行中间件。
如果你想添加需要所有 Node.js API 的逻辑(或使用外部包),可以考虑将此逻辑移至布局作为服务器组件。例如,检查标头和重定向。你也可以使用标头、Cookie 或查询参数通过 next.config.js
进行重定向或重写。如果这不起作用,还可以使用自定义服务器。
环境变量
Next.js 支持构建时和运行时环境变量。
默认情况下,环境变量仅在服务器端可用。要将环境变量暴露给浏览器,必须添加 NEXT_PUBLIC_
前缀。然而,这些公共环境变量会在 next build
期间内联到 JavaScript 包中。
你可以在动态渲染期间安全地在服务器上读取环境变量。
这允许你使用单一的 Docker 镜像,在不同环境中使用不同的值进行升级。
须知:
- 你可以使用
register
函数在服务器启动时运行代码。- 我们不建议使用 runtimeConfig 选项,因为它不适用于独立输出模式。相反,我们建议逐步采用应用路由器。
缓存和 ISR
Next.js 可以缓存响应、生成的静态页面、构建输出以及其他静态资源,如图片、字体和脚本。
缓存和重新验证页面(使用增量静态再生 (ISR))使用相同的共享缓存。默认情况下,此缓存存储在 Next.js 服务器的文件系统(磁盘)上。这在自托管时自动工作,适用于页面路由器和应用路由器。
如果你想将缓存的页面和数据持久化到持久存储中,或在多个容器或 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
以防止用户特定数据被缓存。这适用于应用路由器和页面路由器。这还包括草稿模式。
静态资源
如果你想在不同的域或 CDN 上托管静态资源,可以在 next.config.js
中使用 assetPrefix
配置。Next.js 在检索 JavaScript 或 CSS 文件时将使用此资源前缀。将资源分离到不同域的缺点是额外的 DNS 和 TLS 解析时间。
配置缓存
默认情况下,生成的缓存资源将存储在内存中(默认为 50MB)和磁盘上。如果你使用 Kubernetes 等容器编排平台托管 Next.js,每个 pod 都会有缓存的副本。为了防止显示过时数据(因为默认情况下缓存不在 pod 之间共享),你可以配置 Next.js 缓存以提供缓存处理程序并禁用内存缓存。
在自托管时配置 ISR/数据缓存位置,可以在 next.config.js
文件中配置自定义处理程序:
然后,在项目根目录创建 cache-handler.js
,例如:
使用自定义缓存处理程序可以确保所有托管 Next.js 应用的 pod 之间的一致性。例如,你可以将缓存值保存在任何地方,如 Redis 或 AWS S3。
须知:
revalidatePath
是缓存标签上的便利层。调用revalidatePath
将使用提供的页面的特殊默认标签调用revalidateTag
函数。
构建缓存
Next.js 在 next build
期间生成一个 ID,以标识正在服务的应用版本。相同的构建应该用于启动多个容器。
如果你为环境的每个阶段重新构建,需要在容器之间使用一致的构建 ID。在 next.config.js
中使用 generateBuildId
命令:
版本偏差
Next.js 会自动缓解大多数版本偏差的情况,并在检测到新资源时自动重新加载应用。例如,如果 deploymentId
不匹配,页面之间的转换将执行硬导航而非使用预取的值。
当应用重新加载时,如果未设计为在页面导航之间持久化状态,可能会丢失应用状态。例如,使用 URL 状态或本地存储将在页面刷新后保留状态。然而,像 useState
这样的组件状态会在这种导航中丢失。
流式传输和 Suspense
Next.js 应用路由器在自托管时支持流式响应。如果使用 Nginx 或类似代理,需要配置它禁用缓冲以启用流式传输。
例如,可以通过将 X-Accel-Buffering
设置为 no
来禁用 Nginx 中的缓冲:
部分预渲染
部分预渲染(实验性)默认与 Next.js 一起工作,并非仅限 CDN 的功能。这包括作为 Node.js 服务器(通过 next start
)部署以及使用 Docker 容器时。
与 CDN 一起使用
当在 Next.js 应用前使用 CDN 时,页面在访问动态 API 时将包含 Cache-Control: private
响应标头。这确保生成的 HTML 页面被标记为不可缓存。如果页面完全预渲染为静态,它将包含 Cache-Control: public
以允许页面在 CDN 上缓存。
如果不需要静态和动态组件的混合,可以将整个路由设为静态,并在 CDN 上缓存输出 HTML。如果在运行 next build
时不使用动态 API,这是默认行为。
随着部分预渲染趋于稳定,我们将通过部署适配器 API 提供支持。
after
after
在使用 next start
自托管时完全支持。
停止服务器时,通过发送 SIGINT
或 SIGTERM
信号并等待,确保优雅关闭。这允许 Next.js 服务器等待 after
内部使用的待处理回调函数或 Promise 完成。