跳到内容
返回博客

2020年7月27日,星期一

Next.js 9.5

发布者

我们今天很高兴推出 Next.js 9.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,
  };
}

重新验证标志是发生最多一次生成的时间(秒数),以防止https://en.wikipedia.org/wiki/Cache_stampede

与传统的 SSR 不同,增量静态再生成确保您保留静态化的优势

  • 延迟没有峰值。页面提供始终如一的快速。
  • 页面永不下线。如果后台页面再生成失败,旧页面保持不变。
  • 低数据库和后端负载。页面最多只计算一次“同时”。

增量功能(添加页面和延迟更新它们)以及预览模式,现在都已稳定,并且 next startVercel 边缘平台都开箱即用地完全支持。

为了展示这一新功能,我们创建了一个示例,演示如何重新生成一个静态页面,该页面显示了特定 GitHub 问题的各种表情符号反应计数:https://reactions-demo.vercel.app/

After the first visit following our emoji reaction, a new page generation kicks off in the background. Every single request throughout is served from static cache.
在首次访问并进行表情符号反应后,新的页面生成会在后台启动。整个过程中的每个请求都从静态缓存中提供。

接下来,我们将制定一份补充 RFC,以解决另外两个增量静态生成功能:

  • 同时重新生成并失效多个页面(例如您的博客索引和某篇博客文章)
  • 通过监听事件(例如 CMS webhook)在用户流量到来之前重新生成

有关更多详细信息,请查看getStaticProps 文档

可自定义的基本路径

Next.js 项目并非总是从域名的根路径提供服务。有时您可能希望在子路径(例如 /docs)下托管 Next.js 项目,以便 Next.js 项目仅涵盖域名的该子部分。

虽然这在以前是可能的,但需要相当多的额外配置。例如,为每个 <Link> 添加前缀,并确保 Next.js 从正确的路径提供 JavaScript 捆绑包。

为了解决这个痛点,我们引入了一个新的配置选项。basePath 允许您轻松地将您的 Next.js 项目托管在您域名的子路径上。

要开始使用 basePath,您可以将其添加到 next.config.js

next.config.js
module.exports = {
  basePath: '/docs',
};

配置 basePath 后,您的项目将自动从提供的路径路由。在此示例中为 /docs

当使用 next/linknext/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 将导致在 Web 浏览器中呈现以下 HTML

<a href="/docs/documentation-page">Documentation page</a>

欲了解更多详情,请查阅basePath 文档

支持重写、重定向和头部

重写

构建 Next.js 项目时,您可能希望将某些路由代理到另一个 URL。例如,如果您想逐步将 Next.js 引入您的技术栈,您会希望路由 Next.js 项目中存在的页面,然后将所有未匹配的内容路由到您正在迁移的旧项目。

Next.js 9.5 引入了一个名为 rewrites 的新配置选项,它允许您将传入的请求路径映射到不同的目标路径,包括外部 URL。

例如,您可能希望将某个路由重写为 example.com

next.config.js
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 功能的更多信息,请查看重写文档

重定向

大多数网站至少需要一些重定向。尤其是在更改项目路由结构时。例如,将 /blog 移动到 /news 或类似的转换。

以前,在您的 Next.js 项目中拥有一个重定向列表需要设置自定义服务器或自定义 _error 页面来检查该路由是否设置了重定向。然而,这牺牲了关键的静态和无服务器优化(通过拥有服务器)或不够符合人体工程学。

从 Next.js 9.5 开始,您现在可以在 next.config.js 中,在 redirects 键下创建重定向列表

next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
    ];
  },
};

要了解有关 redirects 功能的更多信息,请查看重定向文档

标头

Next.js 允许您构建同时使用静态生成和服务器端渲染的混合项目。通过服务器端渲染,您可以为传入请求设置标头。对于静态页面,以前无法设置标头。

我们现在在 next.config.js 中引入了一个 headers 属性,它适用于所有 Next.js 路由

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Feature-Policy',
            // Disable microphone and geolocation
            value: "microphone 'none'; geolocation 'none'",
          },
        ],
      },
    ];
  },
};

headers 选项允许您设置常用的头部,例如 Feature-PolicyContent-Security-Policy

要了解有关 headers 功能的更多信息,请查看headers 文档

URL 中可选的尾随斜杠

Next.js 在三年前推出时,其默认行为是所有带有尾随斜杠的 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 自动应用/移除尾部斜杠,以避免不必要的重定向。
next.config.js
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 会更改,所有缓存都会失效。

对于大多数项目来说,这种权衡是可以接受的,但是,我们希望通过不再使未更改页面的浏览器缓存失效来进一步优化此行为。

与 Google Chrome 团队合作开发的Next.js 9.2 中改进的代码分割策略为 Next.js 页面捆绑包生成方面的这些改进奠定了一些基础。

从 Next.js 9.5 开始,所有页面 JavaScript 捆绑包将使用内容哈希而不是构建 ID。这使得在部署之间未更改的页面可以保留在浏览器和边缘缓存中,而无需再次下载。

相比之下,这些更改后的 URL 模式看起来像是

/_next/static/chunks/pages/about.qzfS4o5gIEXRME6sTEahL.js

代替全局构建 ID,qzfS4o5gIEXRME6sTEahL 部分是 about.js 捆绑包的确定性哈希,只要您网站的该部分代码不变,它就会保持稳定。此外,它现在通过 Next.js 自动为您设置的 Cache-Control: public,max-age=31536000,immutable 在重新部署后长期缓存

快速刷新增强

我们在 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 添加了对全捕获动态路由的支持,该功能已被社区广泛采用,用于各种用例。全捕获路由为您提供了更大的灵活性,可以创建由无头 CMS、GraphQL API、文件系统等驱动的高度动态的路由结构。

在听取反馈后,我们了解到用户希望拥有更大的灵活性来匹配路由的最根级别。今天,我们很高兴为这些高级场景推出可选的全捕获动态路由

要创建可选的全捕获路由,您可以使用 [[...slug]] 语法创建页面。

例如,pages/blog/[[...slug]].js 将匹配 /blog,以及其下的任何路由,例如:/blog/a/blog/a/b/c 等等。

与全捕获路由一样,slug 将在路由器查询对象中作为路径部分的数组提供。因此,对于路径 /blog/foo/bar,查询对象将是 { slug: ['foo', 'bar'] }。对于路径 /blog,查询对象将省略 slug 键:{ }

您可以在我们的文档中了解有关可选全捕获路由的更多信息

Webpack 5 支持(测试版)

Webpack 5 目前处于测试阶段。它包含了一些重大改进

我们今天很高兴宣布 Webpack 5 的 Next.js 测试版可用。

要试用 Webpack 5,您可以在 package.json 中使用 Yarn resolution

package.json
{
  "resolutions": {
    "webpack": "^5.0.0-beta.30"
  }
}

Webpack 5 测试版已在生产环境中部署到 nextjs.orgvercel.com。我们鼓励您逐步尝试,并在 GitHub 上报告您的发现。

编译基础设施改进

为了支持 Webpack 5,我们重写了许多编译管道,使其更适合 Next.js

  • Next.js 不再依赖于 webpack-hot-middlewarewebpack-dev-middleware,而是直接使用 webpack 并专门针对 Next.js 项目进行优化。这带来了更简单的架构和更快的开发编译。
  • 按需条目是 Next.js 允许其在开发过程中编译您访问的页面的系统,也已重写,并且通过利用专门为我们的用例量身定制的新 webpack 行为,现在更加可靠。
  • React 快速刷新和 Next.js 错误覆盖现在完全兼容 webpack 5
  • 磁盘缓存将在未来的测试版中启用。

向后兼容性

我们始终致力于确保 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 上,该项目已获得超过 51,100 次星标。

加入 Next.js 社区,参与 GitHub 讨论。讨论是一个社区空间,允许您与其他 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!