跳至内容
返回博客

2019年7月8日,星期一

Next.js 9

发布者

在经历了70个候选版本发布后,我们很高兴地推出 Next.js 9,其主要特性包括:

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

与往常一样,我们一直努力确保所有这些优势都向后兼容。对于大多数 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 的必要类型。

如果不存在,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 片段或漂亮/简洁 URL)是 Next.js 发布 2.5 年后在 GitHub 上收到的第一个功能请求!

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

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

但是,自定义服务器也存在一些缺陷:路由在服务器级别而不是代理级别处理,它作为整体进行部署和扩展,并且容易出现性能问题。

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

此外,我们相信我们可以提供更好的开发人员体验!当您创建一个名为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 首选和混合 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 / “良好的首个问题” 来讨论解决指示工作正在进行的问题的潜在方案。

我们从许多设计师和工程师那里收到了关于 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 请求来改进 Next.js。

这种合作的目标是进行大规模的性能改进,重点关注包大小、启动时间和水合时间。

例如,这些更改将改善小型网站的体验,但也将改善大型应用程序(如HuluTwitchDeliveroo)的体验。

模块/非模块

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

例如,目前 Next.js 必须为async/await语法提供 polyfills,因为代码可能在不支持async/await的浏览器中执行,这会导致错误。

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

为了在将现代 JavaScript 发送到支持它的浏览器时避免破坏旧版浏览器,Next.js 将利用模块/非模块模式。模块/非模块模式提供了一种可靠的机制,用于向现代浏览器提供现代 JavaScript,同时仍然允许旧版浏览器回退到 polyfilled ES5。

Next.js 中模块/非模块的 RFC 可以在此处找到

改进的包拆分

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

Next.js Chunking Collaboration RFC
Next.js 分块合作 RFC

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

其他改进

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

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

社区

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

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

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

  • 我们有超过 720 位贡献者至少提交了 1 次。
  • 在 GitHub 上,该项目已被收藏超过 38,600 次
  • 自第一个版本以来,已提交超过 3,400 个 pull-requests,自上一个主要版本以来已超过 800 个

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

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