跳到内容
返回博客

2019 年 7 月 8 日,星期一

Next.js 9

发布者

经过 70 个 Canary 版本发布后,我们很高兴推出 Next.js 9,其特点包括

  • 内置零配置 TypeScript 支持:借助自动 TypeScript 支持和集成的类型检查,更有信心地构建您的应用程序。
  • 基于文件系统的动态路由:通过文件系统表达复杂的应用程序路由需求,而无需自定义服务器。
  • 自动静态优化:默认情况下创建利用服务器端渲染和静态预渲染的超快速网站,而不会影响功能。
  • API 路由:快速构建后端应用程序端点,利用热重载和统一的构建管道。
  • 更多生产优化:由于视口内预取和其他优化,应用程序的响应速度比以往任何时候都更快。
  • 改进的 DX(开发者体验):不引人注目、易于使用的改进,可帮助您发挥最佳开发水平。

与往常一样,我们一直努力确保所有这些优势都向后兼容。对于大多数 Next.js 应用程序,您只需运行

终端
npm i next@latest react@latest react-dom@latest

在极少数情况下,您的代码库可能需要更改。请参阅升级指南以获取更多信息。

自上次发布以来,我们很高兴看到像 IGNBang & OlufsenIntercomBufferFerrari 等公司使用 Next.js 发布产品。查看案例展示了解更多信息!

内置零配置 TypeScript 支持

一年前,Next.js 6 通过一个名为 @zeit/next-typescript 的插件引入了基本的 TypeScript 支持。用户还需要自定义他们的 .babelrc 并在 next.config.js 中启用它。

配置后,该插件将允许 Next.js 构建 .ts.tsx 文件。但是,它没有集成类型检查,Next.js 核心也没有提供类型。这意味着必须单独维护一个社区包在 DefinitelyTyped 中,它可能与版本发布不同步。

在与许多现有和新用户交谈时,我们清楚地了解到,大多数人对使用 TypeScript 非常感兴趣。他们希望获得更可靠和标准的解决方案,以便轻松地将 TypeScript 集成到他们现有或新的代码库中。

因此,我们着手将 TypeScript 支持集成到 Next.js 核心中,从而改善开发者体验,并在过程中使其更快。

自动化设置

在 Next.js 中开始使用 TypeScript 非常简单:将任何文件、页面或组件从 .js 重命名为 .tsx。然后,运行 next dev

这将导致 Next.js 检测到您的项目正在使用 TypeScript。Next.js CLI 将引导您完成安装 React 和 Node.js 所需的类型。

如果默认的 tsconfig.json 尚不存在,Next.js 也会创建一个具有合理默认值的 tsconfig.json。此文件允许在 Visual Studio Code 等编辑器中进行集成的类型检查。

Next.js 9 自动化 TypeScript 设置
集成类型检查

Next.js 在开发和构建生产环境时都会为您处理类型检查。

在开发过程中,Next.js 将在保存文件后向您显示类型错误。类型检查在后台进行,使您可以立即在浏览器中与更新后的应用程序进行交互。类型错误将在可用时传播到浏览器。

Next.js 9 开发类型检查

如果存在类型错误,Next.js 还会自动使生产构建(即 next build)失败。这有助于防止将损坏的代码发布到生产环境。

Next.js 9 Production Type-Checking
Next.js 9 生产类型检查
Next.js 核心使用 TypeScript 编写

在过去的几个月中,我们将大部分代码库迁移到了 TypeScript,这不仅加强了我们的代码质量,还使我们能够为所有核心模块提供类型。

例如,当您导入 next/link 时,支持 TypeScript 的编辑器将显示允许的属性以及它们接受的值。

Next.js Core Types
Next.js 核心类型

动态路由段

动态路由(也称为 URL Slug 或漂亮/简洁 URL)是 Next.js 在 2.5 年前发布后,GitHub 上最早的功能请求之一!

这个问题在 Next.js 2.0 中通过引入自定义服务器 API 以编程方式使用 Next.js 得到了“解决”。这允许将 Next.js 用作渲染引擎,从而实现抽象和传入 URL 到渲染特定页面的映射。

我们与用户进行了交谈,并检查了他们的许多应用程序,发现他们中的许多人都有自定义服务器。一种模式出现了:自定义服务器最突出的原因是动态路由。

但是,自定义服务器有其自身的缺陷:路由在服务器级别而不是代理级别处理,它作为单体部署和扩展,并且容易出现性能问题。

由于自定义服务器需要整个应用程序在一个实例中可用,因此通常难以部署到解决这些问题的 Serverless 环境。Serverless 请求在代理层路由,并独立扩展/执行,以避免性能瓶颈。

此外,我们相信我们可以提供更好的开发者体验!当您创建一个名为 pages/blog.js 的文件并突然拥有一个可在 /blog 访问的页面时,Next.js 的许多魔力就开始了。

为什么用户需要创建自己的服务器并了解 Next.js 的编程 API 来支持类似 /blog/my-first-post (/blog/:id) 的路由?

基于此反馈和愿景,我们开始研究路由映射解决方案,其驱动力是用户已经知道的内容:pages/ 目录。

创建动态路由页面

Next.js 支持创建带有基本命名参数的路由,这种模式在 path-to-regexp(为 Express 提供支持的库)中流行起来。

现在,可以通过在您的 pages 目录中创建一个名为:pages/post/[pid].js 的文件来创建一个匹配 /post/:pid 路由的页面!

Next.js 将自动匹配类似 /post/1/post/hello-nextjs 等的请求,并渲染在 pages/post/[pid].js 中定义的页面。匹配的 URL 段将作为查询参数传递给您的页面,名称在 [方括号] 之间指定。

例如:给定以下页面和请求 /post/hello-nextjsquery 对象将是 { pid: 'hello-nextjs' }

static async getInitialProps({ query }) {
  // pid = 'hello-nextjs'
  const { pid } = query
 
  const postContent = await fetch(
    `https://api.example.com/post/${encodeURIComponent(pid)}`
  ).then(r => r.text())
 
  return { postContent }
}

还支持多个动态 URL 段!

目录名和文件名都支持 [param] 语法,这意味着以下示例有效

./pages/blog/[blogId]/comments/[commentId].js
./pages/posts/[pid]/index.js

您可以在 Next.js 文档Next.js 学习部分中阅读有关此功能的更多信息。

自动静态优化

Next.js 在大约两年前发布的 v3 版本中添加了对静态网站生成的支持。当时,这是添加到 Next.js 的最受欢迎的功能。

这是有充分理由的:不可否认,静态网站速度很快! 它们不需要服务器端计算,并且可以从 CDN 位置即时流式传输到最终用户。

但是,服务器端渲染或静态生成的应用程序之间的选择是二元的,您要么选择服务器端渲染,要么选择静态生成。没有中间地带。

实际上,应用程序可能具有不同的需求。这些需求需要不同的渲染策略和权衡。

例如,主页和营销页面通常包含静态内容,并且是静态优化的绝佳候选对象。

另一方面,产品仪表板可能会受益于服务器端渲染,因为数据会频繁更新。

我们开始探索如何为用户提供两全其美的体验,并做到默认快速。我们如何为用户提供静态营销页面和动态服务器渲染页面?

从 Next.js 9 开始,用户不再需要在完全服务器渲染或静态导出其应用程序之间做出选择。在每个页面的基础上为您提供两全其美的体验。

自动部分静态导出

引入了一种启发式方法来自动确定页面是否可以预渲染为静态 HTML。

此确定是通过页面是否通过使用 getInitialProps 是否具有阻塞数据需求来做出的。

这种启发式方法允许 Next.js 发出包含服务器渲染页面和静态生成页面的混合应用程序。

内置的 Next.js 服务器 (next start) 和编程 API (app.getRequestHandler()) 都透明地支持此构建输出。无需配置或特殊处理。

静态生成的页面仍然是响应式的:Next.js 将在客户端对您的应用程序进行水合,使其具有完全的交互性。

此外,如果页面依赖于 URL 中的查询参数,Next.js 将在水合后更新您的应用程序。

Next.js 将在开发期间以可视方式通知您页面是否将静态生成。可以通过单击它来隐藏此视觉工件。

Next.js Static Optimization Indicator
Next.js 静态优化指示器

静态生成的页面也将显示在 Next.js 的构建输出中

Next.js Build Output Type Indicator
Next.js 构建输出类型指示器

API 路由

在许多情况下,当构建 React 应用程序时,您最终都需要某种后端。要么从数据库检索数据,要么处理用户提供的数据(例如联系表单)。

我们发现,许多需要后端的用户都使用自定义服务器构建了他们的 API。这样做时,他们遇到了很多问题。例如,Next.js 不会编译自定义服务器代码,这意味着您不能使用 import/export 或 TypeScript。

因此,许多用户最终在自定义服务器之上实现了自己的自定义编译管道。虽然这解决了他们的目标,但它容易出现许多缺陷:例如,配置不正确时,将为他们的整个应用程序禁用 tree shaking。

这提出了一个问题:如果我们把 Next.js 提供的开发者体验带到构建 API 后端会怎么样?

今天,我们很高兴推出 API 路由,这是 Next.js 中用于构建后端的最佳开发者体验。

要开始使用 API 路由,请在 pages/ 目录中创建一个名为 api/ 的目录。

此目录中的任何文件都将自动映射到 /api/<您的路由>,就像其他页面文件映射到路由一样。

例如,pages/api/contact.js 将映射到 /api/contact

注意:API 路由也支持动态路由

pages/api/ 目录中的所有文件都导出一个请求处理程序函数,而不是 React 组件

export default function handle(req, res) {
  res.end('Hello World');
}

通常,API 端点会接收一些传入数据,例如查询字符串、请求正文或 Cookie,并响应其他数据。

在研究向 Next.js 添加 API 路由支持时,我们注意到在许多情况下,用户没有直接使用 Node.js 请求和响应对象。相反,他们使用了服务器库(如 Express)提供的抽象。

这样做的原因是,在许多情况下,传入数据是某种形式的文本,必须先对其进行解析才能有用。因此,这些特定的服务器库有助于消除手动解析数据的负担,最常见的是通过中间件。最常用的中间件提供查询字符串、正文和 Cookie 解析,但是它们仍然需要一些设置才能开始使用。

Next.js 中的 API 路由将默认提供这些中间件,以便您可以立即高效地创建 API 端点

export default function handle(req, res) {
  console.log(req.body); // The request body
  console.log(req.query); // The url querystring
  console.log(req.cookies); // The passed cookies
  res.end('Hello World');
}

除了使用传入数据外,您的 API 端点通常也会返回数据。通常,此响应将是 JSON。Next.js 默认提供 res.json() 以使发送数据更容易

export default function handle(req, res) {
  res.json({ title: 'Hello World' });
}

在开发中更改 API 端点时,代码会自动重新加载,因此无需重启服务器。

生产优化

Next.js 9 将自动预取视口中出现的 <Link> 组件。

此功能通过加快导航到新页面的速度来提高应用程序的响应能力。

Next.js 使用 Intersection Observer预取必要的资源 在后台。

这些请求具有低优先级,并让位于 fetch() 或 XHR 请求。如果用户启用了数据保护程序,Next.js 将避免自动预取。

您可以通过将 prefetch 属性设置为 false 来选择退出很少访问的页面的此功能

<Link href="/terms" prefetch={false}>
  <a>Terms of Service</a>
</Link>

默认优化 AMP

Next.js 9 现在默认情况下为 AMP-first 和混合 AMP 页面渲染优化的 AMP。

虽然 AMP 页面是可选的,但 Next.js 会自动优化其输出。这些优化可以将渲染速度提高多达 50%

此更改得益于 Sebastian BenzAMP Optimizer 上的出色工作。

消除 typeof window 分支的死代码

Next.js 9 在服务器和客户端构建期间将 typeof window 替换为其适当的值(undefinedobject)。此更改允许 Next.js 自动从您的生产构建应用程序中删除死代码。

如果用户在 getInitialProps 或应用程序的其他部分中包含仅服务器端代码,则应该看到他们的客户端捆绑包大小减小。

开发者体验改进

正在编译指示器

在版本 9 之前,了解热代码替换即将发生(以及 Next.js 编译器工具链正在工作)的唯一方法是查看开发者控制台。

然而,很多时候人们都在关注最终的渲染结果,这使得很难知道 Next.js 是否仍在进行编译工作。例如,您可能正在对页面样式进行细微更改,并且您不会立即知道它们是否已更新。

因此,我们创建了一个 RFC / “好的首个 issue” 来讨论指示工作正在进行的问题的潜在解决方案。

我们收到了许多设计师和工程师对 RFC 的反馈,例如他们喜欢什么以及指示器设计的潜在方向。

Rafael Almeida 抓住这个机会与我们的团队合作,并实现了一个全新的指示器,该指示器现在在 Next.js 9 中默认可用。

每当 Next.js 正在进行编译工作时,您都会在页面右下角看到一个小三角形出现!

Next.js 编译指示器

控制台输出

传统上,在开发环境中进行更改时,Next.js 会显示一个编译指示器状态,其中加载状态栏会填充,并在您进行更改时不断清除屏幕。

此行为会导致一些问题。最值得注意的是,它会清除来自您的应用程序代码的控制台输出,例如当您向组件添加 console.log 时。而且,当使用外部工具(如 Vercel CLIdocker-compose)将日志输出拼接在一起时,也会出现此问题。

从 Next.js 9 开始,日志输出跳动减少,并且不再清除屏幕。这允许更好的整体体验,因为您的终端窗口将具有更多相关信息,并且闪烁更少,同时 Next.js 将更好地与您可能已在使用的工具集成。

Next.js 开发控制台输出

特别感谢 Justin Chase 在输出清除方面进行的合作。

构建输出统计信息

使用 next build 为生产环境构建您的应用程序时,它现在将为您提供已构建的所有页面的详细视图。

每个页面都会自动接收一些统计信息。

最突出的是包大小。随着应用程序的增长,您的 JavaScript 包也会增长,这种构建时指示将帮助您指示生产包的增长。将来,您还可以为页面设置性能预算,这将使生产构建失败。

Next.js Built Page Size
Next.js 构建页面大小

除了包大小之外,我们还显示了每个页面中使用了多少项目组件和 node_modules 组件。这指示了页面的复杂性。

Next.js Page Package Count
Next.js 页面包计数

每个页面还指示它是静态优化还是服务器端渲染,因为每个页面的行为可能不同。

Next.js Built Page Type
Next.js 构建页面类型

每页配置对象

现在,每个页面都可以导出一个配置对象。最初,此配置允许您选择加入 AMP,但在将来,您将能够配置更多页面特定的选项。

pages/about.js
export const config = { amp: true };
 
export default function AboutPage(props) {
  return <h3>My AMP About Page!</h3>;
}

要选择加入混合 AMP 渲染,您可以使用值 'hybrid'

pages/about.js
import { useAmp } from 'next/amp';
 
export const config = { amp: 'hybrid' };
 
export default function AboutPage(props) {
  const isAmp = useAmp();
  return <h3>My About Page!{isAmp ? <> Powered by AMP!</> : ''}</h3>;
}

withAmp 高阶组件已删除,取而代之的是这个新的配置。

我们提供了一个 codemod,它可以自动将 withAmp 的用法转换为新的配置对象。您可以在升级指南中阅读有关此内容的更多信息。

代码库改进

我们最近对我们的工具进行了一些更改,以在为代码库做出贡献时提供更好的体验,并确保代码库增长时的稳定性。

正如您在 TypeScript 部分阅读到的那样,Next.js 核心现在用 TypeScript 编写,并且自动为 Next.js 应用程序生成类型以供使用。除了这对使用 Next.js 构建的应用程序有用之外,它在处理核心代码库时也很有用。因为您可以自动获得类型错误和自动完成。

Next.js 已经有一个相当大的集成测试套件,其中包含 50 多个 Next.js 应用程序,并针对它们运行测试。这些测试确保在发布新版本时,升级是平稳的,因为之前可用的功能已针对相同的测试套件进行了测试。

我们的大多数测试都是集成测试,因为在许多情况下,它们复制了“真实”开发人员在开发中使用 Next.js 的情况。例如,我们有测试复制对 Next.js 应用程序进行更改,以查看热模块替换是否有效。

我们的集成测试主要基于 Selenium webdriver,我们将其与 chromedriver 结合使用,以在无头 Chrome 中进行测试。然而,随着时间的推移,其他浏览器,尤其是像 Internet Explorer 11 这样的旧浏览器,会出现某些问题。

由于我们使用了 Selenium,因此我们能够在多个浏览器上自动运行我们的测试。

截至目前,我们正在 Chrome、Firefox、Safari 和 Internet Explorer 11 上运行我们的测试套件。

Google Chrome 合作

Google Chrome 团队一直在通过贡献 RFC 和 pull-requests 来改进 Next.js。

此次合作的目标是大规模的性能改进,重点是包大小、启动和 hydration 时间。

例如,这些更改将改善小型网站的体验,但也改善像 Hulu, Twitch, 和 Deliveroo 这样的大型应用程序的体验。

模块 / 非模块

首要关注的领域是将现代 JavaScript 运送到支持现代 JavaScript 的浏览器。

例如,目前 Next.js 必须为 async/await 语法提供 polyfill 补丁,因为代码可能在不支持 async/await 的浏览器中执行,这将导致崩溃。

Next.js Module/Nomodule Collaboration RFC
Next.js 模块/非模块协作 RFC

为了避免破坏旧浏览器,同时仍然将现代 JavaScript 发送到支持它的浏览器,Next.js 将利用 module/nomodule 模式。 module/nomodule 模式为现代浏览器提供现代 JavaScript,同时仍然允许旧浏览器回退到 polyfill 补丁的 ES5,从而提供了一种可靠的机制。

Next.js 中 module/nomodule 的 RFC 可以在此处找到

改进的包拆分

Next.js 中当前的包拆分策略基于一种基于比率的启发式方法,用于将模块包含在单个“commons”块中。由于只有一个 bundle,因此粒度非常小,代码要么不必要地下载(因为 commons 块可能包含特定路由实际上不需要的代码),要么代码在多个页面 bundle 中重复。

Next.js Chunking Collaboration RFC
Next.js Chunking 协作 RFC

改进的包拆分的 RFC 可以在此处找到

其他改进

Chrome 团队还在进行许多其他优化和更改,这些更改将改进 Next.js。这些 RFC 将很快分享。

这些 RFC 和 pull-requests 被标记为“Collaboration”,以便可以在 Next.js issue 跟踪器中轻松找到它们。

社区

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

此版本有超过 65 位 pull-request 作者贡献了核心改进或示例。

说到示例,我们现在提供了 200 多个关于如何将 Next.js 与不同库和技术集成的示例!包括大多数 css-in-js 和数据获取库。

  • 我们有超过 720 位贡献者 至少提交了 1 次 commit。
  • 在 GitHub 上,该项目已被 star 超过 38,600 次
  • 自首次发布以来,已提交了超过 3,400 个 pull requests,自上次主要版本以来就超过了 800 个

自上次主要版本以来,Next.js 社区已经翻了一番,拥有超过 8,600 名成员加入我们!

我们感谢我们的社区以及所有外部反馈和贡献,这些反馈和贡献帮助塑造了此版本。