2019年9月30日,星期一
Next.js 9.0.7
发布者Next.js 9.0 大约在两个月前发布。从那时起,我们一直忙于 7 个较小但非常重要的版本:9.0.1、9.0.2、9.0.3、9.0.4、9.0.5、9.0.6 和 9.0.7。
让我们深入了解这些版本为您的网站和应用程序带来了什么,并且绝对没有破坏性更改。
- 改进 Windows 环境中的并发性:
next build
进程现在在 Windows 上更加可靠,并且可以更好地并行处理工作。 - 默认启用 Gzip 压缩:现在默认添加了 Gzip 压缩,以减少所需的优化步骤。
- 仅在活动页面上报告 TypeScript:内置的 TypeScript 支持已扩展为仅监视活动页面上的更改,从而使其更快、更可靠。
- 遥测:将帮助我们专注于 Next.js 的哪些部分需要优化,并验证优化是否具有预期的效果。
- 改进
next/head
元素跟踪:next-head
类已被删除,这使得实现某些验证其实现的工具变得更容易。 - 防止 Pages 目录中出现非页面文件:通过防止意外发布非页面文件,优化您的应用程序安全性和
next build
时间。 - 运行时改进:当不使用 Next.js 的某些部分时,例如
next/dynamic
,它们将不再在运行时被需要,从而减小了 bundle 大小。
改进 Windows 环境中的并发性
Next.js 在 next build
过程中在许多地方执行并发工作。主要用途是使用 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)处理。反向代理从单线程 Node 进程中移除 CPU 密集型工作。
但是,在检查 Web 上 Next.js 的使用情况时,我们发现很大一部分公司没有配置任何压缩。
在 Vercel 等平台上,gzip
和 brotli
在代理级别自动处理。
当自托管时,公司必须自己添加 gzip(通过 compression
或反向代理)。
从 Next.js 9.0.4 开始,当使用 next start
或自定义 server.js
时,默认包含 gzip
压缩。
brotli
支持即将推出,因为 Node.js 现在原生支持 Brotli。
如果您的应用程序已通过自定义服务器启用压缩,则 Next.js 不会添加自己的压缩器。
默认情况下,Next.js 不包含 serverless 目标的压缩,因为当使用 serverless 目标时,资产是单独上传的,而不是通过 Node.js 提供的。
如果您部署在处理压缩的平台(如 Vercel)上,则无需进行任何更改。
仅在活动页面上报告 TypeScript
Next.js 9 包含对 TypeScript 的内置支持。所有必要的步骤只是将单个页面从 .js
重命名为 .tsx
。Next.js 将自动处理或引导您完成任何剩余的设置。
Next.js 还通过并行于开发过程运行 tsc --watch
来处理类型检查。在开发过程中,Next.js 有一个称为 按需入口 的概念。此功能仅编译您正在积极处理的页面。

从 9.0.4 开始,Next.js 现在仅报告按需入口正在积极编译的页面的类型错误。这减少了大量的类型检查噪音,同时专注于一组特定的页面。
完整的应用程序类型检查仍然在 next build
期间运行,或者可以在您的编辑器中/由您的编辑器处理。
遥测
Next.js 发布至今已近 3 年,在这 3 年中,该框架得到了显著发展,从新功能到所有用户更好的默认设置。
我们一直以来改进此过程的方式在很大程度上是手动进行的。
Vercel 有一些大型 Next.js 应用程序。例如,vercel.com、vercel.com/docs 和 https://nextjs.net.cn。我们一直在 Vercel 内部“吃自己的狗粮” 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
模块公开。
例如,要向页面添加标题
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
属性有时会使从外部服务添加的脚本无法验证。
Gerald Monaco,来自 Google Chrome 团队,提出了一种方法,可以在不需要元素上的类名的情况下保留清理行为。
新行为是 Next.js 将插入一个额外的 <meta>
标签,其中包含它 (next/head
) 渲染的元素数量。这样,Next.js 可以使用计数来清理现有元素。
因此,当将多个元素注入到页面的 <head>
中时,此方法减少了初始 HTML 有效负载大小。
防止 Pages 目录中出现非页面文件
刚开始使用 Next.js 时,您要做的第一件事是创建一个 pages
目录。
约定是 pages
目录中的每个文件都成为应用程序中的路由。一个简单的示例是 pages/about.js
变为 /about
。
随着时间的推移,我们偶尔收到报告,称用户的应用程序(无论大小)构建性能较差。
经过进一步调查,发现这些用户已在 pages
目录中创建了整个组件结构。
由于 pages
目录中的每个文件都被视为页面,因此每个组件都被编译为应用程序中的页面。这会导致大量的构建时开销,为无效页面生成 2 个以上的 JavaScript 文件。
此外,这将部分影响 Next.js 决定如何生成代码拆分 chunk。这是因为 Next.js 使用关于跨页面的库使用情况的启发式方法。
因此,我们必须确保用户不会将此缺陷引入他们的 Next.js 应用程序中。
Next.js 9 现在验证 pages
目录中的文件是否导出了 React 组件。
在实际操作中,这意味着 Next.js 将向您显示一条消息,警告您在 pages
目录中找到了潜在的非页面文件。
这鼓励用户将不是页面的文件移动到另一个目录中。反过来,开发、生产和代码拆分更快、更准确。
运行时改进
Next.js 框架由许多部分组成。其中之一是客户端运行时。此运行时处理 hydration、客户端路由等。
Hydration(此改进的重点)是使服务器渲染或预渲染的 HTML 具有交互性所必需的。Hydration 添加事件处理程序并调用生命周期方法,如 useEffect()
或 componentDidMount
- 使您的应用程序为最终用户做好准备。
此外,Next.js 处理的不仅仅是基本的 hydration - 例如,设置客户端路由器、配置 next/head
以及通过 next/dynamic
加载额外的应用程序逻辑。
这些职责中的每一个也都有自己特定的运行时部分。
在 next/dynamic
的情况下,Next.js 必须确保在服务器上渲染的延迟加载组件在客户端上已准备就绪。每次使用 next/dynamic
都会生成一个额外的 JavaScript bundle,并且这些文件必须在 hydration 之前加载,以避免 hydration 不匹配。
以前,此运行时将始终包含在 Next.js 运行时 bundle 中。现在,仅当您在应用程序中使用 next/dynamic
时才包含它。这意味着对于不使用 next/dynamic
的应用程序,下载、解析和执行的 JavaScript 更少。
AppTree 支持
React 生态系统中的某些库以非常特定的方式实现服务器端渲染。最值得注意的是,Apollo 的服务器端渲染解决方案(称为 getDataFromTree
)的工作原理是渲染 React 树,对于找到的每个 Query
,它将等待结果,然后再次重新渲染 React 树。
默认情况下,Next.js 向 React 树添加一些上下文值,例如,可以使用 useRouter
读取的路由器。
with-apollo
示例过去渲染 React 树的方式是通过渲染 <App>
并尝试手动填充缺失的属性。随着 React Context 的添加,这不再可能,因为上下文提供程序是一个单独的元素。
从 Next.js 9.0.4 开始,在 getInitialProps
中的上下文对象中添加了一个名为 AppTree
的新属性。它是专门为外部库必须遍历整个 React 树(如 Apollo 的 getDataFromTree
)的情况而添加的。
with-apollo
示例已更新以反映更改。如果您已经在应用程序中实现了 Apollo,建议更新到 AppTree
方法,以便 useRouter
和未来添加的其他 API 在您的 Next.js 应用程序中正常工作。
如果您没有使用 Apollo 或类似的库,我们建议尽量避免使用 AppTree,因为 Next.js 团队不建议通常遍历 React 树。它会增加相当多的性能开销,因为 React 树会被多次渲染,而不是只渲染一次。
移除 next-server
包
当我们在一年多前开始为 serverless 部署优化 Next.js 时,我们创建了一个名为 next-server
的包。这个包是实验性的,与 next
包一起发布。它从未公开记录,但它是一项创建尽可能小的 Next.js 服务器运行时的实验。
最终,该包取得了成功,确实缩小了生产服务器运行时。然而,我们想出了一种创新的新方法,通过 Next.js 编译器和 静态分析,使运行时更小。
这样做之后,next-server
变得过时,并被 serverless target 取代。与使用 next-server
包作为 next
的替代品相比,此目标具有更优化的输出。
虽然这个包已经过时且无法直接使用,但我们仍然保留了它。这是因为它有一些在包之间共享的内部组件,并且移动代码需要相当大的工作量。
我们最近为此付出了努力,并将代码从 next-server
移回了 next
包。这意味着 Next.js 框架的所有代码现在都存在于 next
包中。
这使得初学者和经验丰富的贡献者都更容易为 Next.js 做出贡献。现在只有一个编译过程和统一的构建配置。以前,next
和 next-server
有单独的设置,以及对哪些代码属于每个包的任意约束。
升级 Next.js
如果您的项目运行在旧版本的 Next.js 上,我们建议升级到 Next.js 9。
在大多数情况下,升级不需要进行任何更改。您可以按照升级指南来确保平稳的升级体验。
我们要感谢所有社区贡献者,他们更新了自发布以来的指南。
未来的发展方向?
这篇博文中概述的新优化只是我们一直在努力进行的更广泛的优化和功能的开始。
我们将很快分享关于正在进行的 RFC 的更新。在此之前,您可以通过 GitHub 上的 RFC 标签 获得一些先睹为快的机会。
这展示了我们一直在研究的一些功能,例如 内置 CSS 支持,公共目录支持,和 src 目录支持。
社区
我们很高兴看到 Next.js 社区的持续增长。
- 我们有超过 800 位贡献者提交了至少 1 次 commit。
- 在 GitHub 上,该项目已被 star 超过 41,100 次。
自上次主要版本发布以来,Next.js 社区规模翻了一番,拥有超过 10,900 名成员。加入我们!
我们对社区的持续贡献以及来自公司和用户的外部反馈感到兴奋,这些反馈有助于塑造版本发布。