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
)时,运行时将不再需要它们,从而减小捆绑包大小。
改进 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
属性有时会导致从外部服务添加的脚本无法验证。
来自 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 框架由许多部分组成。其中之一是客户端运行时。此运行时处理 hydration、客户端路由等。
hydration(此改进的重点)是使服务器渲染或预渲染的 HTML 交互所必需的。Hydration 添加事件处理程序并调用生命周期方法,如 useEffect()
或 componentDidMount
—— 使您的应用程序为最终用户做好准备。
此外,Next.js 处理的不只是基本的 hydration —— 例如,设置客户端路由器,配置 next/head
,以及通过 next/dynamic
加载额外的应用程序逻辑。
这些职责中的每一个也都有其特定的运行时部分。
在 next/dynamic
的情况下,Next.js 必须确保在服务器上渲染的延迟加载组件在客户端上已准备就绪。每次使用 next/dynamic
都会生成一个额外的 JavaScript 捆绑包,并且这些文件必须在 hydration 之前加载,以避免 hydration 不匹配。
以前,此运行时将始终包含在 Next.js 运行时捆绑包中。现在,仅当您在应用程序中使用 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
的新属性。它是专门为外部库必须像 Apollo 的 getDataFromTree
那样遍历整个 React 树的情况而添加的。
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
变得过时,并被 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 名成员。加入我们!
我们对社区的持续贡献以及来自公司和用户的外部反馈感到兴奋,这些反馈有助于塑造版本发布。