跳到内容
返回博客

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通过引入自定义服务器API以编程方式使用Next.js来“解决”了这个问题。这允许将Next.js用作渲染引擎,从而实现抽象并将传入的URL映射到渲染某些页面。

我们与用户进行了交流并检查了他们的许多应用程序,发现其中许多应用程序都包含自定义服务器。一个模式浮出水面:自定义服务器最主要的原因是动态路由。

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

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

此外,我们相信我们可以提供更好的开发体验!Next.js 的许多魔力都始于您创建一个名为 pages/blog.js 的文件,然后突然就可以通过 /blog 访问该页面。

为什么用户需要创建自己的服务器并学习 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 和拉取请求来改进 Next.js。

此次合作的目标是实现大规模的性能改进,主要集中在捆绑包大小、启动和水合时间。

例如,这些更改将改善小型网站的体验,同时也将改善像HuluTwitchDeliveroo这样的大型应用程序的体验。

模块 / 无模块

第一个重点是向支持现代 JavaScript 的浏览器提供现代 JavaScript。

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

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

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

Next.js 中模块/非模块的 RFC 可以在这里找到

改进的捆绑包拆分

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

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

改进的捆绑包拆分 RFC 可以在这里找到

其他改进

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

这些 RFC 和拉取请求被标记为“协作”,以便在 Next.js 问题跟踪器中轻松找到。

社区

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

此版本有超过 65 位拉取请求作者贡献了核心改进或示例。

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

  • 我们已有超过 **720 位贡献者**提交了至少一次提交。
  • 在 GitHub 上,该项目已获得超过 **38,600 次标星**。
  • 自首次发布以来,已提交超过 **3,400 个拉取请求**,自上次主要发布以来,这一数字已超过 **800**!

自上次主要发布以来,Next.js 社区的成员数量翻了一番,达到超过 **8,600 名成员**。加入我们吧!

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