2020年7月27日,星期一
Next.js 9.5
发布者今天我们很高兴地推出 Next.js 9.5,其特点包括
- 稳定的增量静态再生:在部署后重新构建静态页面,只需毫秒
- 可自定义的基础路径:轻松在域名的子路径上托管 Next.js 项目
- 支持重写、重定向和标头:重写自定义 URL,重定向旧 URL,并向静态页面添加标头
- URL 中可选的尾部斜杠:一致地强制执行尾部斜杠的缺失或存在
- 页面包的持久缓存:未更改页面的 JavaScript 文件现在可以在构建之间保留
- 快速刷新增强:提高了 Next.js 实时编辑体验的可靠性
- 生产环境 React 性能分析:一个用于衡量项目渲染 '成本' 的新标志
- 可选的 Catch All 路由:动态路由现在为 SEO 驱动的用例提供更大的灵活性
- Webpack 5 支持(beta 版):可选择加入下一版本的 webpack 5 以提高构建大小和速度
稳定的增量静态再生
Next.js 在 9.3 版本中引入了静态站点生成方法,其目标明确:我们应该获得静态的优势(始终快速、始终在线、全球复制),同时对动态数据提供出色的支持,而这正是 Next.js 所擅长的。
为了兼顾两者的优点,Next.js 引入了增量静态生成,在您构建站点后更新静态内容。通过在 getStaticPaths
中使用 fallback: true
选项,您可以在运行时注册 新的静态页面。
Next.js 可以通过这种方式按需静态预渲染无限数量的页面,无论您的数据集有多大。
今天,我们宣布增量静态再生成正式发布,这是一种更新现有页面的机制,通过在后台重新渲染页面,随着流量的进入而更新。
受 stale-while-revalidate 的启发,后台重新生成确保流量不间断地提供服务,始终来自静态存储,并且只有在新页面生成完成后才推送。
export async function getStaticProps() {
return {
props: await getDataFromCMS(),
// we will attempt to re-generate the page:
// - when a request comes in
// - at most once every second
revalidate: 1,
};
}
revalidate
标志是最多发生一次重新生成的时间秒数,以防止 https://en.wikipedia.org/wiki/Cache_stampede。
与传统的 SSR 不同,增量静态再生确保您保留静态的优势
- 延迟无峰值。页面始终以快速的速度提供服务。
- 页面永不离线。如果后台页面重新生成失败,旧页面将保持不变。
- 低数据库和后端负载。页面最多同时重新计算一次。
增量功能(添加页面和延迟更新页面)以及 预览模式 现在都已稳定,并且开箱即用地完全支持 next start
和 Vercel 边缘平台。
为了展示这项新功能,我们创建了一个示例,展示了重新生成静态页面,该页面显示特定 issue 的各种 GitHub reaction 的计数:https://reactions-demo.vercel.app/

接下来,我们将致力于补充 RFC,以解决另外两个增量静态生成功能
- 一次重新生成和使多个页面失效(例如您的博客索引和某个博客文章)
- 通过监听事件(例如 CMS webhook)在用户流量之前进行重新生成
有关更多详细信息,请查看 getStaticProps
文档。
可自定义的基础路径
Next.js 项目并非始终从域名的根目录提供服务。有时您可能希望在子路径(如 /docs
)下托管 Next.js 项目,以便 Next.js 项目仅覆盖域名的该子部分。
虽然到目前为止这是可能的,但这需要付出相当多的额外配置。例如,将前缀添加到每个 <Link>
并确保 Next.js 从正确的路径提供 JavaScript 包。
为了解决这个痛点,我们引入了一个新的配置选项。basePath
允许您轻松地在域名的子路径上托管 Next.js 项目。
要开始使用 basePath
,您可以将其添加到 next.config.js
中
module.exports = {
basePath: '/docs',
};
配置 basePath
后,您的项目将自动从提供的路径路由。在本例中为 /docs
。
当使用 next/link
或 next/router
链接到项目中的其他页面时,basePath
将自动添加前缀。这允许您在不更改项目的情况下更改 basePath
。
一个示例是使用 next/link
路由到另一个页面
import Link from 'next/link';
export default function HomePage() {
return (
<>
<Link href="/documentation-page">
<a>Documentation page</a>
</Link>
</>
);
}
以这种方式使用 next/link
将导致以下 HTML 渲染到 Web 浏览器
<a href="/docs/documentation-page">Documentation page</a>
有关更多详细信息,请查看 basePath
文档。
支持重写、重定向和标头
重写
在构建 Next.js 项目时,您可能希望将某些路由代理到另一个 URL。例如,如果您想逐步将 Next.js 采用到您的技术栈中,您可能希望路由 Next.js 项目中存在的页面,然后将所有不匹配的内容路由到您正在迁移的旧项目。
在 Next.js 9.5 中,我们引入了一个名为 rewrites
的新配置选项,它允许您将传入的请求路径映射到不同的目标路径,包括外部 URL。
例如,您可能希望将某个路由重写为 example.com
module.exports = {
async rewrites() {
return [
{ source: '/backend/:path*', destination: 'https://example.com/:path*' },
];
},
};
在这种情况下,/backend
下的所有路径都将路由到 example.com
。
您还可以检查您的 Next.js 项目路由是否匹配,如果没有匹配,则重写到之前的项目。这对于逐步采用 Next.js 非常有用
module.exports = {
async rewrites() {
return [
// check if Next.js project routes match before we attempt proxying
{
source: '/:path*',
destination: '/:path*',
},
{
source: '/:path*',
destination: `https://example.com/:path*`,
},
];
},
};
在这种情况下,我们首先匹配所有路径。如果没有匹配,我们将代理到 example.com
,这将是之前的项目。
要了解有关 rewrites
功能的更多信息,请查看 rewrites 文档。
重定向
大多数网站至少需要一些重定向。尤其是在更改项目路由结构时。例如,当将 /blog
移动到 /news
或类似的过渡时。
以前在 Next.js 项目中拥有重定向列表需要设置自定义服务器或自定义 _error
页面来检查路由是否设置了重定向。然而,这以使关键的静态和无服务器优化失效(通过拥有服务器)为代价,或者不够符合人体工程学。
从 Next.js 9.5 开始,您现在可以在 next.config.js
中的 redirects
键下创建重定向列表
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true,
},
];
},
};
要了解有关 redirects
功能的更多信息,请查看 redirects 文档。
标头
Next.js 允许您构建混合项目,这些项目同时使用静态生成和服务器端渲染。使用服务器端渲染,您可以为传入的请求设置标头。对于静态页面,直到现在才有可能设置标头。
我们现在在 next.config.js
中引入了一个 headers
属性,该属性适用于所有 Next.js 路由
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Feature-Policy',
// Disable microphone and geolocation
value: "microphone 'none'; geolocation 'none'",
},
],
},
];
},
};
headers
选项允许您设置常用的标头,如 Feature-Policy
和 Content-Security-Policy
。
要了解有关 headers
功能的更多信息,请查看 headers 文档。
URL 中可选的尾部斜杠
当 Next.js 在 3 年前推出时,它的默认行为是所有带有尾部斜杠的 URL 始终返回 404 页面。
虽然有效,但一些用户已请求更改此行为的能力。例如,当将现有项目迁移到以前始终强制执行尾部斜杠的 Next.js 时。
在 Next.js 9.5 中,我们引入了一个名为 trailingSlash
的新选项,用于 next.config.js
。
这个新选项确保 Next.js 自动处理尾部斜杠行为
- 自动将尾部斜杠 URL 重定向到没有尾部斜杠的 URL,例如:
/about/
到/about
- 当
trailingSlash
设置为true
时,没有尾部斜杠的 URL 将重定向到带有尾部斜杠的 URL,例如:/about
到/about/
- 确保
next/link
自动应用/删除尾部斜杠,以避免不必要的重定向。
module.exports = {
// Force a trailing slash, the default value is no trailing slash (false)
trailingSlash: true,
};
要了解有关 trailingSlash
功能的更多信息,请查看 trailingSlash 文档
页面包的持久缓存
在编写 Next.js 页面时,所有脚本包、CSS 样式表和 HTML 的创建都是完全自动化的,并从您那里抽象出来。如果您在 Next.js 9.5 之前检查生成的 <script>
标签,您会注意到它们的 URL 遵循这样的模式
/_next/static/ovgxWYrvKyjnlM15qtz7h/pages/about.js
上面的路径段 ovgxWYrvKyjnlM15qtz7h
是我们称之为构建 ID 的内容。虽然这些文件很容易在边缘和用户的机器上缓存,但在重新构建您的应用程序后,构建 ID 会更改,并且所有缓存都将被清除。
对于大多数项目来说,这种权衡是可以接受的,但是,我们希望通过不再使未更改页面的浏览器缓存失效来进一步优化此行为。
Next.js 9.2 中引入的 改进的代码拆分策略 是与 Google Chrome 团队合作开发的,为 Next.js 页面包生成的这些改进奠定了一些基础。
从 Next.js 9.5 开始,所有页面 JavaScript 包都将使用内容哈希而不是构建 ID。这允许在部署之间未更改的页面保留在浏览器和边缘缓存中,而无需再次下载。
相比之下,这些更改后的 URL 模式看起来像这样
#pages/about.qzfS4o5gIEXRME6sTEahL.js
qzfS4o5gIEXRME6sTEahL
部分不是全局构建 ID,而是 about.js
包的确定性哈希,只要您网站该部分的代码不更改,它就会保持稳定。此外,它现在通过 Cache-Control: public,max-age=31536000,immutable
在重新部署之间长期缓存,Next.js 会自动为您设置。
快速刷新增强
我们在 Next.js 9.4 中引入了快速刷新,这是一种新的热重载体验,可让您即时获得对 React 组件所做编辑的反馈。
Next.js 9.5 进一步完善了我们的快速刷新实现,并为您提供了成功所需的工具
- 易于理解的错误:所有编译和运行时错误都已更新为 仅显示相关信息,包括导致错误的任何代码的代码框架。
- 开发时保持组件状态的提示:Next.js 现在为您提供有用的提示,以确保快速刷新在尽可能多的场景中保持您的组件状态。Next.js 提供的每个提示都是完全可操作的,并附带前后示例!
- 组件状态重置时的警告:当 Next.js 在文件编辑后无法保持组件状态时,我们现在将打印详细的警告。此警告将帮助您诊断项目为何必须重置组件状态,从而使您能够修复它并充分利用快速刷新。
- 新文档:我们 添加了广泛的文档,解释了什么是快速刷新、它的工作原理以及期望什么!该文档还将通过解释其错误恢复的工作原理来教您如何更好地利用快速刷新。
- 用户代码故障排除指南:新文档还包括 常见故障排除步骤和有关如何在开发中充分利用快速刷新的提示。
生产环境 React 性能分析
React 在不久前引入了 Profiler API,它允许您跟踪 React 组件中的性能问题。虽然此功能在开发中自动工作,但它需要使用单独版本的 ReactDOM 才能在生产环境中进行性能分析。
在 Next.js 9.5 中,您现在可以使用 next build
中的 --profile
标志为 React 启用生产环境性能分析
next build --profile
之后,您可以像在开发中一样使用性能分析器。
要了解有关 React 性能分析的更多信息,您可以阅读 React 团队关于 React Profiler 的帖子。特别感谢 TODOrTotev 和 @darshkpatel 为此功能的贡献。
可选的 Catch All 路由
Next.js 9.2 添加了 对 catch-all 动态路由的支持,该路由已被社区广泛采用,用于各种用例。Catch-all 路由使您可以灵活地创建由 Headless CMS、GraphQL API、文件系统等驱动的高度动态的路由结构。
在听取反馈意见后,我们了解到用户希望拥有更大的灵活性来匹配路由的最根级别。今天,我们很高兴为这些高级场景推出可选的 catch-all 动态路由。
要创建可选的 catch-all 路由,您可以使用 [[...slug]]
语法创建一个页面。
例如,pages/blog/[[...slug]].js
将匹配 /blog
,以及其下的任何路由,例如:/blog/a
、/blog/a/b/c
等。
与 catch-all 路由一样,slug
将在 路由器查询对象 中作为路径部分的数组提供。因此,对于路径 /blog/foo/bar
,查询对象将是 { slug: ['foo', 'bar'] }
。对于路径 /blog
,查询对象将省略 slug 键:{ }
。
您可以在我们的 文档中了解有关可选 catch all 路由的更多信息。
Webpack 5 支持(beta 版)
Webpack 5 目前处于 beta 版。它包含一些重大改进
- 改进的 Tree-Shaking:嵌套导出、内部模块和 CommonJS 都进行了 tree shaking
- 持久缓存:允许重用以前构建的工作
- 确定性的 chunk 和模块 ID:解决 webpack 模块 ID 在构建之间会更改的情况
今天我们很高兴地宣布 webpack 5 for Next.js 的 beta 版本可用。
要试用 webpack 5,您可以在 package.json
中使用 Yarn resolutions
{
"resolutions": {
"webpack": "^5.0.0-beta.30"
}
}
Webpack 5 beta 版本已在 nextjs.org 和 vercel.com 上线生产环境。我们鼓励您以渐进的方式尝试,并在 GitHub 上反馈您的发现。
编译基础设施改进
为了支持 webpack 5,我们重写了许多编译管道,使其更适合 Next.js。
- Next.js 不再依赖
webpack-hot-middleware
和webpack-dev-middleware
,而是直接使用 webpack 并专门为 Next.js 项目进行优化。这转化为更简单的架构和更快的开发编译速度。 - 按需入口 (On-demand-entries),这是 Next.js 用来允许在您在开发期间访问的页面上进行编译的系统,也已重写,并且现在通过利用专门为我们的用例量身定制的新的 webpack 行为,变得更加可靠。
- React Fast Refresh 和 Next.js 错误覆盖层 现在与 webpack 5 完全兼容
- 磁盘缓存将在未来的 beta 版本中启用。
向后兼容性
我们始终致力于确保 Next.js 向后兼容以前的版本。
Webpack 4 将继续获得完全支持。我们正在与 webpack 团队紧密合作,以确保从 webpack 4 到 5 的迁移尽可能顺利。
如果您的 Next.js 项目没有自定义 webpack 配置,则无需进行任何项目更改即可完全利用 webpack 5。
重要提示: 如果您的项目有自定义 webpack 配置,则可能需要进行一些更改才能过渡到 webpack 5。我们建议密切关注我们的迁移说明,或尽可能减少 webpack 扩展的使用,以便将来实现无缝升级。
改进了 macOS 上的文件监听
我们最近发现 webpack 存在一个问题,即在 macOS 上,对代码进行少量更改后,文件监听会停止。您必须手动重启项目才能再次看到更新。进行几次更改后,该循环会重复。
此外,我们发现这个问题不仅发生在 Next.js 项目中,而且发生在所有基于 webpack 构建的项目和框架中。
经过几天的调试,我们追踪到其根本原因在于 webpack 使用的文件监听实现 chokidar,这是一种在 Node.js 生态系统中广泛使用的文件监听实现。
我们向 chokidar 提交了一个补丁 来修复此问题。在该补丁发布后,我们与 Tobias Koppers 合作,在 新的 webpack 版本 中发布了这个补丁。
当您升级到 Next.js 9.5 时,将自动使用此修补后的 webpack 版本。
结论
我们很高兴看到 Next.js 的采用率持续增长
- 我们拥有超过 1,200 位独立贡献者**,** 自 9.4 版本发布以来新增了超过 135 位贡献者。
- 在 GitHub 上,该项目已被 Star 超过 51,100 次。
加入 Next.js 社区,请访问 GitHub Discussions。Discussions 是一个社区空间,您可以在其中与其他 Next.js 用户联系,并自由提问或分享您的工作。
例如,您不妨先与大家分享您的项目 URL。
如果您想回馈社区但又不确定如何做,我们鼓励您尝试我们的 Webpack 支持等实验性功能,并反馈您的发现!
鸣谢
我们感谢我们的社区,包括所有帮助塑造此版本的外部反馈和贡献。
特别感谢 Jan Potoms,他是 Next.js 社区的长期成员,为本版本的多个功能做出了贡献。
特别感谢 webpack 的作者 Tobias Koppers,他帮助 Next.js 实现了 webpack 5 的支持。
此版本由以下贡献者共同完成:@chandan-reddy-k、@Timer、@aralroca、@artemisart、@sospedra、@prateekbh、@Prioe、@Janpot、@merceyz、@ijjk、@PavelK27、@marbiano、@MichelleLucero、@thorsten-stripe、@TODOrTotev、@Skn0tt、@lfades、@timneutkens、@akhila-ariyachandra、@chibicode、@rafaelalmeidatk、@kirill-konshin、@jamesvidler、@JeffersonBledsoe、@tylev、@jamesmosier、@filipemarins、@Remeic、@vvo、@timothyis、@jazibsawar、@coetry、@adam-zacharski、@danwilliams、@tywmick、@matamatanot、@goldins、@mvllow、@its-tayo、@sshyam-gupta、@wilbert-abreu、@sebastianbenz、@jaydenseric、@developit、@dylanjha、@darshkpatel、@spinks、@stefanprobst、@moh12594、@jasonmerino、@cristiand391、@HyunSangHan、@mcsdevv、@M1ck0、@hydRAnger、@alexej-d、@valmassoi、@motleydev、@eKhattak、@jpedroschmitz、@JerryGoyal、@bowen31337、@phillip055、@balazsorban44、@chuabingquan、@youhosi、@andresz1、@bell-steven、@areai51、@Wssn、@ndom91、@anthonyshort、@zxzl、@jbowes、@IamLizu、@PascalPixel、@ralphilius、@ysun62、@muslax、@elsigh、@AsherFoster、@botv、@tomdohnal、@christianalfoni、@tomasztunik、@gsimone、@illuminist、@jplew、@OskarKaminski、@RickyAbell、@steph-query、@ericgoe、@MalvinJay、@cristianbote、@Ashikpaul、@jensmeindertsma、@amorriscode、@abhik-b、@awareness481、@LukasPolak、@arvigeus、@romMidnight、@jackyef、@drumm2k、@kuldeepkeshwar、@bogy0、@Belco90、@wawjr3d、@tanmaylaud、@SarKurd、@kevinsproles、@dstotijn、@styfle、@blackwright、@BrunoBernardino、@heyAyushh、@Necmttn、@TrySound、@obedparla、@NyashaNziramasanga、@tonyspiro、@kukicado、@ceorourke、@MehediH、@robintom、@karlhorky 和 @tcK1!