如何自托管您的 Next.js 应用程序
在部署您的 Next.js 应用程序时,您可能需要根据您的基础设施配置如何处理不同的功能。
🎥 观看: 了解更多关于自托管 Next.js 的信息 → YouTube (45 分钟)。
图片优化
通过 next/image
进行图片优化在部署时使用 next start
自托管无需任何配置。如果您希望使用单独的服务来优化图片,可以配置图片加载器。
通过在 next.config.js
中定义自定义图片加载器,图片优化可用于静态导出。请注意,图片是在运行时优化的,而不是在构建期间。
须知
代理
当使用 next start
进行部署时,代理在自托管环境下无需任何配置即可工作。由于它需要访问传入请求,因此在使用静态导出时不支持。
代理使用Edge Runtime,它是可用 Node.js API 的子集,有助于确保低延迟,因为它可能在应用程序的每个路由或资产之前运行。如果您不希望这样,可以使用完整的 Node.js Runtime 来运行代理。
如果您希望添加需要所有 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, 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
函数在服务器启动时运行代码。
缓存和 ISR
Next.js 可以缓存响应、生成的静态页面、构建输出以及图片、字体和脚本等其他静态资源。
缓存和重新验证页面(通过增量静态生成)使用相同的共享缓存。默认情况下,此缓存存储在 Next.js 服务器的文件系统(磁盘)上。使用 Pages 和 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 上托管静态资产,可以使用 next.config.js
中的 assetPrefix
配置。Next.js 在检索 JavaScript 或 CSS 文件时将使用此资产前缀。将资产分离到不同的域会带来 DNS 和 TLS 解析额外耗时的问题。
配置缓存
默认情况下,生成的缓存资产将存储在内存中(默认为 50MB)和磁盘上。如果您使用 Kubernetes 等容器编排平台托管 Next.js,每个 pod 都将拥有缓存的副本。为了防止由于缓存默认情况下不共享而显示过期数据,您可以配置 Next.js 缓存以提供缓存处理程序并禁用内存缓存。
要在自托管时配置 ISR/数据缓存位置,您可以在 next.config.js
文件中配置自定义处理程序。
module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // disable default in-memory caching
}
然后,在项目根目录创建 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
命令
module.exports = {
generateBuildId: async () => {
// This could be anything, using the latest git hash
return process.env.GIT_HASH
},
}
版本偏差
Next.js 将自动缓解大多数版本偏差情况,并在检测到时自动重新加载应用程序以获取新资产。例如,如果 deploymentId
不匹配,页面之间的转换将执行硬导航,而不是使用预取值。
重新加载应用程序时,如果应用程序状态未设计为在页面导航之间持久化,则可能会丢失应用程序状态。例如,使用 URL 状态或本地存储将在页面刷新后持久化状态。但是,像 useState
这样的组件状态将在这种导航中丢失。
流式传输和 Suspense
Next.js App Router 在自托管时支持流式响应。如果您使用 Nginx 或类似的代理,您需要配置它来禁用缓冲以启用流式传输。
例如,您可以通过将 X-Accel-Buffering
设置为 no
来禁用 Nginx 中的缓冲。
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
当使用 next start
自托管时,after
得到完全支持。
当停止服务器时,请通过发送 SIGINT
或 SIGTERM
信号并等待来确保优雅关机。这允许 Next.js 服务器等待 after
中使用的挂起回调函数或 Promise 完成。
这有帮助吗?