部署
恭喜你,现在是时候部署到生产环境了。
你可以选择 使用 Vercel 托管 Next.js,或者在 Node.js 服务器、Docker 镜像,甚至静态 HTML 文件上进行自托管。当使用 next start
部署时,支持所有的 Next.js 功能。
生产构建
运行 next build
会为生产环境生成一个优化版本的应用。基于你的页面会生成 HTML、CSS 和 JavaScript 文件。JavaScript 会使用 Next.js 编译器 进行编译,浏览器包会被压缩,以帮助达到最佳性能并支持所有现代浏览器。
Next.js 生成的标准部署输出可用于托管和自托管的 Next.js。这确保了两种部署方式都支持所有功能。在下一个主要版本中,我们将把这个输出转换为我们的 Build Output 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"
脚本:
然后,运行 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 支持所有 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
中定义自定义图片加载器来与静态导出一起使用。注意,图片是在运行时而不是在构建时优化的。
值得注意的是:
Middleware
使用 next start
部署时,Middleware 可以零配置自托管。由于它需要访问传入请求,在使用静态导出时不支持。
Middleware 使用运行时,这是所有可用 Node.js API 的一个子集,以确保低延迟,因为它可能在应用的每个路由或资源之前运行。这个运行时不需要在"边缘"运行,可以在单区域服务器中工作。要在多个区域运行 Middleware 需要额外的配置和基础设施。
如果你想添加需要所有 Node.js API 的逻辑(或使用外部包),你可以将这个逻辑移到布局中作为服务器组件。例如,检查头信息和重定向。你也可以通过 next.config.js
使用头信息、cookie 或查询参数来重定向或重写。如果这些方法都不适用,你还可以使用自定义服务器。
环境变量
Next.js 支持构建时和运行时环境变量。
默认情况下,环境变量只在服务器端可用。要将环境变量暴露给浏览器,必须以 NEXT_PUBLIC_
为前缀。但是,这些公共环境变量会在 next build
期间内联到 JavaScript 包中。
你可以在动态渲染期间在服务器端安全地读取环境变量。
这允许你使用单个 Docker 镜像,可以在具有不同值的多个环境中部署。
值得注意的是:
- 你可以使用
register
函数在服务器启动时运行代码。- 我们不推荐使用 runtimeConfig 选项,因为它不适用于独立输出模式。相反,我们建议逐步采用 App Router。
缓存和 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 都会有一个缓存副本。为了防止由于缓存默认不在 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
这样的组件状态会在这样的导航中丢失。
Vercel 为 Next.js 应用提供额外的偏差保护,以确保即使在部署新版本后,旧客户端仍然可以访问前一个版本的资源和功能。
你可以在 next.config.js
文件中手动配置 deploymentId
属性,以确保每个请求使用 ?dpl
查询字符串或 x-deployment-id
头。
流式传输和 Suspense
Next.js App Router 在自托管时支持流式响应。如果你使用 Nginx 或类似的代理,你需要配置它以禁用缓冲以启用流式传输。
例如,你可以通过将 X-Accel-Buffering
设置为 no
来在 Nginx 中禁用缓冲:
部分预渲染
部分预渲染(实验性)默认与 Next.js 一起工作,这不是 CDN 功能。这包括通过 next start
部署为 Node.js 服务器,以及在使用 Docker 容器时。
CDN 使用
当在 Next.js 应用前面使用 CDN 时,在访问动态 API 时,页面将包含 Cache-Control: private
响应头。这确保生成的 HTML 页面被标记为不可缓存。如果页面完全预渲染为静态,它将包含 Cache-Control: public
以允许页面在 CDN 上缓存。
如果你不需要静态和动态组件的混合,你可以使你的整个路由静态并在 CDN 上缓存输出 HTML。当运行 next build
时,如果不使用动态 API,这种自动静态优化是默认行为。