部署
恭喜,是时候将应用发布到生产环境了。
您可以选择 使用 Vercel 托管 Next.js,或者通过 Node.js 服务器、Docker 镜像甚至静态 HTML 文件进行自托管。当使用 next start
部署时,所有 Next.js 功能都得到支持。
生产构建
运行 next build
会为生产环境生成优化后的应用版本。系统会根据您的页面创建 HTML、CSS 和 JavaScript 文件。JavaScript 会被 编译,浏览器包会通过 Next.js 编译器 进行 最小化,以实现最佳性能并支持 所有现代浏览器。
Next.js 生成的标准化部署输出同时适用于托管和自托管方案。这确保所有功能在两种部署方式下都能得到支持。在下一个主要版本中,我们将把这个输出转换为我们的 构建输出 API 规范。
使用 Vercel 托管 Next.js
Vercel 作为 Next.js 的创建者和维护者,为您的 Next.js 应用提供托管基础设施和开发者体验平台。
部署到 Vercel 无需配置,并能提供额外的可扩展性、可用性和全球性能增强。不过,所有 Next.js 功能在自托管时仍然可用。
了解更多关于 Vercel 上的 Next.js 或 免费部署模板 来试用。
自托管
您可以通过三种方式自托管 Next.js:
Node.js 服务器
Next.js 可以部署到任何支持 Node.js 的托管提供商。确保您的 package.json
包含 "build"
和 "start"
脚本:
然后运行 npm run build
构建您的应用。最后运行 npm run start
启动 Node.js 服务器。此服务器支持所有 Next.js 功能。
Docker 镜像
Next.js 可以部署到任何支持 Docker 容器的托管提供商。当部署到容器编排平台如 Kubernetes 或在任何云提供商的容器中运行时,可以使用此方法。
- 在您的机器上安装 Docker
- 克隆我们的示例(或 多环境示例)
- 构建容器:
docker build -t nextjs-docker .
- 运行容器:
docker run -p 3000:3000 nextjs-docker
通过 Docker 运行的 Next.js 支持所有功能。
静态 HTML 导出
Next.js 支持从静态站点或单页应用 (SPA) 开始,后续可选择升级使用需要服务器的功能。
由于 Next.js 支持 静态导出,它可以部署到任何能提供 HTML/CSS/JS 静态资源的 web 服务器上,包括 AWS S3、Nginx 或 Apache 等工具。
以 静态导出 方式运行不支持需要服务器的 Next.js 功能。了解更多。
须知:
- 服务端组件 (Server Components) 在静态导出时仍受支持。
功能
图片优化
通过 next/image
实现的 图片优化 在使用 next start
自托管时无需配置即可工作。如果您希望使用单独的服务优化图片,可以 配置图片加载器 (image loader)。
通过 静态导出 也可以使用图片优化,只需在 next.config.js
中定义自定义图片加载器。注意图片是在运行时而非构建时优化的。
须知:
中间件
中间件 (Middleware) 在使用 next start
自托管时无需配置即可工作。由于需要访问传入请求,它不支持 静态导出。
中间件使用的 运行时 (runtime) 是可用 Node.js API 的子集,以确保低延迟,因为它可能运行在应用的每个路由或资源前。此运行时不需要在 "边缘 (edge)" 运行,可在单区域服务器工作。运行多区域中间件需要额外配置和基础设施。
如果您需要添加使用全部 Node.js API 的逻辑(或外部包),可以考虑将此逻辑移至 布局 (layout) 作为 服务端组件 (Server Component)。例如检查 headers 和 重定向 (redirect)。您也可以通过 next.config.js
使用 headers、cookies 或查询参数进行 重定向 或 重写 (rewrite)。如果不可行,还可以使用 自定义服务器 (custom server)。
环境变量
Next.js 支持构建时和运行时环境变量。
默认情况下,环境变量仅在服务端可用。要在浏览器中暴露环境变量,必须添加 NEXT_PUBLIC_
前缀。但这些公共环境变量会在 next build
时内联到 JavaScript 包中。
要读取运行时环境变量,我们推荐使用 getServerSideProps
或 逐步采用应用路由器 (App Router)。通过应用路由器,我们可以在动态渲染期间安全地在服务端读取环境变量。这允许您使用单一的 Docker 镜像,在不同环境中通过不同值进行升级。
须知:
- 您可以使用
register
函数 在服务端启动时运行代码。- 我们不推荐使用 runtimeConfig 选项,因为它不适用于独立输出模式。相反,我们推荐 逐步采用 应用路由器。
缓存与 ISR
Next.js 可以缓存响应、生成的静态页面、构建输出以及其他静态资源如图片、字体和脚本。
缓存和重新验证页面(使用增量静态再生 (ISR) 或应用路由器中的新功能)使用 共享缓存。默认情况下,此缓存存储在 Next.js 服务器的文件系统(磁盘)上。自托管时自动生效,同时支持页面路由器和应用路由器。
您可以配置 Next.js 缓存位置,以将缓存页面和数据持久化到持久存储,或在多个容器或 Next.js 应用实例间共享缓存。
自动缓存
- Next.js 为真正不可变资源设置
Cache-Control
头为public, max-age=31536000, immutable
。此设置不可覆盖。这些不可变文件包含文件名中的 SHA 哈希,因此可以安全地无限期缓存。例如,静态图片导入 (Static Image Imports)。您可以 配置图片的 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
以防止用户特定数据被缓存。这适用于应用路由器和页面路由器。也包括 草稿模式 (Draft Mode)。
静态资源
如果您想在不同域名或 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
是缓存标签 (cache tags) 上的便利层。调用revalidatePath
会使用页面的特殊默认标签调用revalidateTag
函数。
构建缓存
Next.js 在 next build
时生成 ID 以识别正在提供的应用版本。相同的构建应被用于启动多个容器。
如果为环境的每个阶段重新构建,需要在容器间使用一致的构建 ID。在 next.config.js
中使用 generateBuildId
命令:
版本偏差
Next.js 会自动缓解大多数 版本偏差 (version skew) 的情况,并在检测到不匹配时自动重新加载应用以获取新资源。例如,如果 deploymentId
不匹配,页面间的转换会执行硬导航而非使用预取值。
当应用重新加载时,如果未设计为在页面导航间持久化状态,可能会丢失应用状态。例如,使用 URL 状态或本地存储会在页面刷新后保留状态。但组件状态如 useState
会在这种导航中丢失。
Vercel 为 Next.js 应用提供额外的 偏差保护 (skew protection),确保即使新版本部署后,旧版本客户端仍可访问旧版本的资源和函数。
您可以手动在 next.config.js
中配置 deploymentId
属性,确保每个请求使用 ?dpl
查询字符串或 x-deployment-id
头。
手动优雅关闭
在自托管场景下,您可能需要在服务器收到 SIGTERM
或 SIGINT
信号时执行关闭代码。
您可以将环境变量 NEXT_MANUAL_SIG_HANDLE
设为 true
,然后在 _document.js
文件中注册对应的信号处理器。注意需要直接在 package.json
的脚本中注册该环境变量,而非在 .env
文件中设置。
须知:手动信号处理在
next dev
模式下不可用。