当部署您的 Next.js 应用程序时,您可能希望根据您的基础设施配置不同功能的处理方式。
🎥 观看: 了解更多关于自托管 Next.js → YouTube (45 分钟)。
自托管时,建议在 Next.js 服务器前使用反向代理(如 nginx),而不是直接将其暴露在互联网上。反向代理可以处理格式错误的请求、慢速连接攻击、负载大小限制、速率限制以及其他安全问题,将这些任务从 Next.js 服务器卸载。这使得服务器能够将其资源专用于渲染,而非请求验证。
通过 next/image 进行的图像优化在部署时使用 next start,无需任何配置即可自托管工作。如果您希望使用单独的服务来优化图像,您可以配置图像加载器。
图像优化可以通过在 next.config.js 中定义自定义图像加载器来与静态导出一起使用。请注意,图像是在运行时优化的,而不是在构建期间。
须知:
代理在使用 next start 部署时无需任何配置即可自托管工作。由于它需要访问传入的请求,因此当使用静态导出时不支持。
代理使用Edge runtime,它是所有可用 Node.js API 的一个子集,有助于确保低延迟,因为它可能在您应用程序的每个路由或资产之前运行。如果您不希望这样,您可以使用完整的 Node.js runtime 来运行代理。
如果您希望添加需要所有 Node.js API 的逻辑(或使用外部包),您可能可以将此逻辑作为服务器组件移动到布局中。例如,检查请求头和重定向。您还可以通过 next.config.js 使用请求头、cookie 或查询参数进行重定向或重写。如果这不起作用,您还可以使用自定义服务器。
Next.js 可以支持构建时和运行时环境变量。
默认情况下,环境变量仅在服务器端可用。 要将环境变量暴露给浏览器,它必须以 NEXT_PUBLIC_ 为前缀。然而,这些公共环境变量将在 next build 期间内联到 JavaScript 包中。
要读取运行时环境变量,我们建议使用 getServerSideProps 或渐进式采用 App Router。
您可以在动态渲染期间安全地在服务器上读取环境变量。
import { connection } from 'next/server'
export default async function Component() {
await connection()
// cookie、请求头和其他动态 API
// 也会选择动态渲染,这意味着
// 这个环境变量会在运行时进行评估
const value = process.env.MY_VALUE
// ...
}import { connection } from 'next/server'
export default async function Component() {
await connection()
// cookie、请求头和其他动态 API
// 也会选择动态渲染,这意味着
// 这个环境变量会在运行时进行评估
const value = process.env.MY_VALUE
// ...
}这允许您使用一个单独的 Docker 镜像,该镜像可以在多个具有不同值的环境中进行推广。
须知:
- 您可以使用
register函数在服务器启动时运行代码。
Next.js 可以缓存响应、生成的静态页面、构建输出以及其他静态资产,如图像、字体和脚本。
缓存和重新验证页面(通过增量静态再生)使用相同的共享缓存。默认情况下,此缓存存储在 Next.js 服务器的文件系统(磁盘)上。当使用 Pages Router 和 App Router 进行自托管时,这会自动工作。
如果您希望将缓存的页面和数据持久化到持久存储中,或在 Next.js 应用程序的多个容器或实例之间共享缓存,您可以配置 Next.js 缓存位置。
public, max-age=31536000, immutable 的 Cache-Control 请求头设置为真正不可变的资产。它不能被覆盖。这些不可变文件在文件名中包含 SHA 散列,因此它们可以被安全地无限期缓存。例如,静态图像导入。您可以配置图像的 TTL。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 都将拥有缓存的副本。为防止显示陈旧数据(因为缓存默认不在 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) {
// 这可以存储在任何地方,例如持久存储
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].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 命令:
module.exports = {
generateBuildId: async () => {
// 这可以是任何东西,例如使用最新的 git hash
return process.env.GIT_HASH
},
}Next.js 将自动缓解大多数版本偏差的情况,并在检测到时自动重新加载应用程序以检索新资产。例如,如果 deploymentId 不匹配,页面之间的转换将执行硬导航,而不是使用预取值。
当应用程序重新加载时,如果应用程序状态未设计为在页面导航之间持久化,则可能会丢失。例如,使用 URL 状态或本地存储可以在页面刷新后持久化状态。然而,像 useState 这样的组件状态将在这些导航中丢失。
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 容器一起使用时。
当在 Next.js 应用程序前使用 CDN 时,当访问动态 API 时,页面将包含 Cache-Control: private 响应头。这确保了生成的 HTML 页面被标记为不可缓存。如果页面完全预渲染为静态,它将包含 Cache-Control: public 以允许页面在 CDN 上缓存。
如果您不需要静态和动态组件的混合,您可以将整个路由设置为静态并将输出 HTML 缓存在 CDN 上。如果不使用动态 API,这种自动静态优化是运行 next build 时的默认行为。
随着部分预渲染走向稳定,我们将通过 Deployment Adapters API 提供支持。
afterafter 在使用 next start 进行自托管时得到完全支持。
停止服务器时,通过发送 SIGINT 或 SIGTERM 信号并等待,确保正常关闭。这允许 Next.js 服务器等待 after 中使用的待处理回调函数或 Promise 完成。
自托管时,您可能希望在服务器因 SIGTERM 或 SIGINT 信号关闭时运行代码。
您可以将环境变量 NEXT_MANUAL_SIG_HANDLE 设置为 true,然后在 _document.js 文件中为该信号注册一个处理程序。您需要直接在 package.json 脚本中注册环境变量,而不是在 .env 文件中。
须知:手动信号处理在
next dev中不可用。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "NEXT_MANUAL_SIG_HANDLE=true next start"
}
}if (process.env.NEXT_MANUAL_SIG_HANDLE) {
process.on('SIGTERM', () => {
console.log('收到 SIGTERM: 正在清理')
process.exit(0)
})
process.on('SIGINT', () => {
console.log('收到 SIGINT: 正在清理')
process.exit(0)
})
}