跳至内容
返回博客

2019年9月30日,星期一

Next.js 9.0.7

发布者

Next.js 9.0 大约两个月前发布。从那时起,我们一直在忙于发布 7 个较小但非常重要的版本:9.0.19.0.29.0.39.0.49.0.59.0.6以及9.0.7

让我们深入了解这些版本为您的网站和应用程序带来了什么,并且绝对没有重大更改。

改进 Windows 环境中的并发性

next build 过程中,Next.js 在许多地方执行并发工作。主要用途是使用 Terser 并行地最小化构建输出。

以前,这项工作是通过使用名为 worker-farm 的包在多个 CPU 上处理的。但是,我们注意到许多 Windows 用户已使用自定义 webpack 配置禁用了缩小功能。经过进一步检查,我们发现 worker-farm 在 Windows 计算机上无法始终如一地工作。

为了解决此问题,我们从 worker-farm 迁移到 jest-worker。这确保了构建在 macOS、Linux 和 Windows 计算机上都可靠且一致。

顾名思义,jest-worker 是 Jest 测试运行程序的一部分。它是 Jest 用于并行化测试用例的包。这意味着此包经过了充分的测试,可靠且维护良好。

jest-worker 还支持worker_threads,这是 Node 12 中的一项新功能。与 child_process 不同,worker_threads 可以共享内存,这意味着在新的 Node 版本上构建速度更快。

通过切换到 jest-worker,我们能够为 Windows 用户重新启用构建并发。

默认启用 Gzip 压缩

在调查公司使用自定义服务器的原因时,我们发现最常见的原因是压缩。公司会添加一个名为 compression 的 Express 中间件,它负责处理 HTTP 响应的 Gzip 压缩。

此中间件压缩响应,以便向用户发送的字节数更少。通常,这应该由 Nginx 等反向代理来处理。反向代理将 CPU 密集型工作从单线程 Node 进程中移除。

但是,在检查网络上 Next.js 的使用情况时,我们发现很大一部分公司没有配置任何压缩。

在像Vercel这样的平台上,gzipbrotli 会在代理级别自动处理。

在自行托管时,公司必须自己添加 gzip(通过 compression 或反向代理)。

从 Next.js 9.0.4 开始,使用 next start 或自定义 server.js 时,默认情况下会启用 gzip 压缩。

brotli 支持即将推出 即将推出,因为 Node.js 现在原生支持 Brotli。

如果您的应用程序已通过自定义服务器启用了压缩,则 Next.js 不会添加自己的压缩器。

Next.js 默认情况下不包含无服务器目标的压缩,因为使用无服务器目标时,资产会单独上传,而不是通过 Node.js 提供服务。

如果您在处理压缩的平台(例如 Vercel)上部署,则无需进行任何更改。

仅对活动页面进行 TypeScript 报告

Next.js 9 内置支持 TypeScript。您只需将单个页面从 .js 重命名为 .tsx 即可。Next.js 会自动处理或指导您完成任何剩余的设置。

Next.js 还通过在开发过程中并行运行 tsc --watch 来处理类型检查。在开发过程中,Next.js 有一个称为 按需加载入口 的概念。此功能仅编译您正在积极处理的页面。

The on-demand entries flow
按需加载入口流程

从 9.0.4 开始,Next.js 现在仅报告由按需加载入口积极编译的页面的类型错误。这减少了许多类型检查噪音,同时专注于特定的一组页面。

完整的应用程序类型检查仍然在 next build 期间运行,或者可以在您的编辑器中/通过您的编辑器进行处理。

遥测

Next.js 几乎在 3 年前发布,并且在过去的 3 年里,该框架有了很大的发展,从新功能到为所有用户提供更好的默认设置。

我们一直采用这种改进流程的方式非常手动。

Vercel 有几个大型的 Next.js 应用程序。例如,vercel.comvercel.com/docshttps://nextjs.net.cn。我们一直在内部使用 Next.js 内部使用,并根据我们的经验改进了 Next.js。

最重要的是,我们积极与社区互动以收集反馈。您可能之前与 Tim 沟通过,以提供有关您在公司中如何使用 Next.js 的反馈。

例如,如果您使用自定义服务器,如果您有自定义 webpack 配置等等。这些反馈对于指导 Next.js 的功能开发非常有价值。

但是,这种方法存在一个问题,那就是我们只能收集一部分用户的反馈。这部分用户可能与您/您的公司有不同的需求和用例。

一个例子就是导入 CSS 文件,这并不标准,但相当一部分用户似乎正在使用它,无论是通过 next-css(或 Sass/Less),还是通过自定义配置。如果我们知道使用这种特定方法的用户百分比,我们就可以优先考虑改进它。

因此,我们引入了一种匿名、更自动化的收集这些反馈点的方法,以便我们能够在不久的将来进一步改进 Next.js。

这还将使我们能够验证对框架的改进是否提高了所有应用程序的基线。

您可以在 nextjs.org/telemetry 上阅读有关遥测的更多信息。您还可以在其中找到如何选择退出(如果您不想参与)。

使用指示活动的点构建反馈

在与 Next.js 用户交谈时,一个小的反馈项目是,有时看起来 next build 似乎没有做任何事情,主要是视觉上的。

为了解决这个问题,我们在运行 next build 期间向控制台输出添加了一个加载指示器。此输出直观地表明命令仍在运行,并且进程未冻结。

我们计划在可能的情况下扩展此构建输出以显示构建的更多阶段。

新的构建指示点

改进的 next/head 元素跟踪

Next.js 提供了一种内置方式来管理 <head> 元素,因为它负责渲染应用程序的 HTML。此 API 通过 next/head 模块公开。

例如,要向页面添加标题

pages/index.js
import Head from 'next/head';
 
export default function MyPage() {
  return (
    <>
      <Head>
        <title>My Title</title>
      </Head>
      <h1>Hello World</h1>
    </>
  );
}

渲染到 HTML 时,Next.js 会收集在 <Head> 中渲染的所有组件,并将标签渲染到页面的 <head> 中。

但是,Next.js 允许使用 <Link> 组件进行单页应用程序 (SPA) 类型路由转换。

当您点击 <Link> 时,Next.js 会获取渲染客户端页面所需的 JavaScript 文件。然后,它会渲染与该文件关联的 React 组件。

由于此转换发生在客户端,因此我们必须清理从前一个页面注入的 <head> 元素,否则过时的元素可能会出现在另一个页面上。

以前,Next.js 通过向每个 <Head> 提供的元素添加类名来跟踪这些元素。

以以上示例为例,<head> 将如下所示

<head>
  <title class="next-head">My Title</title>
</head>

此解决方案运行良好,因为通过 next/head 注入的每个元素都已清楚地标记,并且易于清理。

但是,一小部分用户报告了问题,即元素上的额外 class 属性有时会导致从外部服务添加的脚本无法验证。

来自 Google Chrome 团队的 Gerald Monaco 提出了一种方法,可以在不需要元素上的类名的情况下保留清理行为。

新的行为是 Next.js 会插入一个额外的 <meta> 标签,其中包含它(next/head)渲染的元素数量。有了这个,Next.js 可以使用计数来清理现有元素。

因此,这种方法减少了将多个元素注入页面 <head> 时的初始 HTML 负载大小。

防止 Pages 目录中的非页面

开始使用 Next.js 时,首先要做的是创建一个 pages 目录。

约定是 pages 目录中的每个文件都成为应用程序中的一个路由。一个简单的例子是 pages/about.js 成为 /about

随着时间的推移,我们偶尔会收到报告,称用户(无论是大型还是小型)的应用程序构建性能不佳。

经过进一步调查,我们发现这些用户在pages目录中创建了他们的整个组件结构。

由于pages目录中的每个文件都被视为一个页面,因此每个组件都被编译为应用程序中的一个页面。这会导致大量的构建时间开销,为无效页面生成2个或更多JavaScript文件。

此外,这还会部分影响Next.js如何决定生成代码分割块。这是因为Next.js使用关于页面之间库用法的启发式方法。

因此,我们必须确保用户不会在他们的Next.js应用程序中引入此陷阱。

Next.js 9现在会验证pages目录中的文件是否导出React组件。

实际上,这意味着Next.js会显示一条消息,提醒您在pages目录中发现了一个潜在的非页面文件。

这鼓励用户将不是页面的文件移动到另一个目录。反过来,开发、生产和代码分割将更快、更准确。

运行时改进

Next.js框架由许多部分组成。其中之一是客户端运行时。此运行时处理水合、客户端路由等。

水合(此改进的重点)是使服务器渲染或预渲染的HTML具有交互性的必要条件。水合添加事件处理程序并调用生命周期方法,例如useEffect()componentDidMount,使您的应用程序准备好供最终用户使用。

此外,Next.js处理的不仅仅是基本的水合——例如,设置客户端路由器,配置next/head,以及通过next/dynamic加载其他应用程序逻辑。

这些职责中的每一个也都有其自身的特定运行时部分。

next/dynamic的情况下,Next.js必须确保在服务器端渲染的延迟加载组件在客户端也已准备就绪。每个next/dynamic的使用都会生成一个额外的JavaScript包,并且必须在水合之前加载这些文件以避免水合不匹配。

以前,此运行时始终包含在Next.js运行时包中。现在,只有在应用程序中使用next/dynamic时才包含它。这意味着对于不使用next/dynamic的应用程序,下载、解析和执行的JavaScript更少。

AppTree支持

React生态系统中的一些库以非常特定的方式实现服务器端渲染。最值得注意的是,Apollo的服务器端渲染解决方案(称为getDataFromTree)的工作原理是渲染React树,并为找到的每个Query等待结果,然后重新渲染React树。

默认情况下,Next.js会将一些上下文值添加到React树中,例如可以使用useRouter读取的路由器。

with-apollo示例以前通过渲染<App>并尝试手动填充缺失的属性来渲染React树。随着React Context的添加,这不再可能,因为上下文提供程序是一个单独的元素。

从Next.js 9.0.4开始,一个名为AppTree的新属性被添加到getInitialProps中的上下文对象中。它是专门为外部库必须遍历整个React树(如Apollo的getDataFromTree)的情况而添加的。

with-apollo示例已更新以反映这些更改。如果您已经在您的应用程序中实现了Apollo,建议您更新到AppTree方法,以便将来添加的useRouter和其他API能够在您的Next.js应用程序中正确工作。

如果您没有使用Apollo或类似的库,我们建议您尝试避免使用AppTree,因为Next.js团队通常不建议遍历React树。它会增加相当多的性能开销,因为React树会被渲染多次而不是一次。

删除next-server

当我们一年前开始为无服务器部署优化Next.js时,我们创建了一个名为next-server的包。此包是实验性的,并与next包一起发布。它从未公开记录过,但它是一个创建尽可能小的Next.js服务器运行时的实验。

最终,该包取得了成功,并且确实使生产服务器运行时更小。但是,我们想出了一个创新的新方法,可以通过Next.js编译器和静态分析使运行时更小。

这样做,next-server变得过时了,并被无服务器目标取代。与使用next-server包替换next相比,此目标具有更优化的输出。

虽然此包已过时且无法直接使用,但我们一直保留它。这是因为它有一些在包之间共享的内部内容,并且移动代码需要大量的努力。

我们最近付出了这些努力,并将代码从next-server移回next包。这意味着Next.js框架的所有代码现在都位于next包中。

这使得初学者和经验丰富的贡献者都能更容易地为Next.js做出贡献。现在只有一个编译过程和统一的构建配置。以前,nextnext-server有单独的设置,以及关于每个包中包含哪些代码的任意约束。

升级Next.js

如果您的项目正在运行旧版本的Next.js,我们建议您升级到Next.js 9。

在大多数情况下,升级不需要进行任何更改。您可以按照升级指南确保顺利升级。

我们要感谢所有社区贡献者自发布以来更新了指南。

未来有什么计划?

本文档中概述的新优化只是我们一直在进行的更广泛优化和功能的开始。

我们很快就会分享正在进行的RFC的更新。在此之前,您可以通过GitHub上的RFC标签获得一些抢先体验。

这展示了我们一直在研究的一些功能,例如内置CSS支持公共目录支持src目录支持

社区

我们很高兴看到Next.js社区的持续发展。

  • 我们有超过800位贡献者至少提交了一次代码。
  • 在GitHub上,该项目获得了超过41,100个星标。

自上次主要版本发布以来,Next.js 社区规模翻倍,成员超过 10,900 人。 加入我们!

我们很高兴看到社区持续贡献,以及来自公司和用户的外部反馈,这些都帮助塑造了版本的发布。