经过足足70个canary版本之后,Next.js 9终于正式发布了!

源自 | Next.js Blog 译者 | 王强 编辑 | Yonie 经过足足 70 个 canary 版本之后,Next.js 9 正式版终于发布了。新版的主要特性包括:

  • 内置零配置 TypeScript 支持:开发者可以借助自动化 TypeScript 支持和集成的类型检查更方便地构建应用。
  • 基于文件系统的动态路由:无需自定义服务器即可通过文件系统表达复杂的应用路由需求。
  • 自动静态优化:利用默认的服务端渲染和静态预渲染提升网站速度,同时不牺牲功能。
  • API 路由: 利用热重载和统一的构建管道快速构建后端应用程序端点。
  • 更多生产优化:通过视口内预取等优化提升应用的响应能力。
  • 提升开发体验:一系列细微、易用的改进,改善开发工作体验。 这些优势都会尽可能向后兼容。大多数 Next.js 应用只需运行下列代码即可升级到新版:
npm i next@latest react@latest react-dom@latest

应用代码库需要改动的情况很少。详细信息可参阅升级指南: https://github.com/zeit/next.js/blob/canary/UPGRADING.md 基于 Next.js 的网站案例很多,包括 IGN、Bang&Olufsen、Intercom、Buffer 和 Ferrari 等等。更多案例可参见: https://nextjs.org/showcase。

内置零配置 TypeScript 支持

一年前的 Next.js 6 通过名为 @zeit/next-typescript 的插件引入了 TypeScript 的基础支持。用户还必须自定义他们的.babelrc 并在 next.config.js 中启用它。 这个插件配置好后允许 Next.js 构建.ts 和.tsx 文件。但它没有集成类型检查,也没有 Next.js 核心提供的类型。于是开发者必须在 DefinitelyTyped 中单独维护社区包,可能没法及时更新内容。 据调研,大多数新老用户都对使用 TypeScript 非常感兴趣。他们需要一种更可靠、更标准化的解决方案,从而将 TypeScript 轻松集成到现有或新的代码库中。 所以 Next.js 核心开始集成 TypeScript 支持,改善了开发人员的体验,并加快了相关流程。 自动安装 在 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 生产类型检查 用 TypeScript 编写的 Next.js 核心 现在 Next.js 的大部分代码库已经迁移到 TypeScript 上,不仅增强了 Next.js 的代码质量,而且现在所有核心模块都提供了类型。 例如,当导入 next/link 时,支持 TypeScript 的编辑器将显示允许的属性以及它们接受的值。 Next.js 核心类型

动态路由

动态路由(也称为 URL Slugs 或 Pretty/Clean URL)是两年半前 Next.js 发布后,GitHub 上的第一批功能请求之一。 Next.js 2.0 引入了自定义服务器 API,开发者能以编程方式使用 Next.js,“解决”了这个需求。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-nextjs 请求,查询对象将是{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 { pos
```tContent }
}
新版还支持多个动态 URL 段。
目录名称和文件名支持 [param] 语法,示例如下:

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

更多信息可参考Next.js 文档或Next.js 学习部分。
相关链接:
Next.js 文档: https://github.com/zeit/next.js#dynamic-routing
Next.js 学习部分: https://nextjs.org/learn/basics/clean-urls-with-dynamic-routing
### 自动静态优化
Next.js 在大约两年前发布的 v3 版本中增加了对静态网站生成的支持。
理由很简单:静态网站速度很快,它们不需要服务器端计算,数据可以立即从 CDN 服务器传输到最终用户。
但是,之前的版本中服务端呈现或静态生成的应用程序不可兼得,要么选择服务端呈现要么是静态生成,没有中间答案。
实际上应用可以有不同的需求。这些需求需要不同的呈现策略和取舍。
例如,主页和营销页面通常包含静态内容,是静态优化的理想场景。
另一方面,产品介绍页可能更适合数据频繁更新的服务端呈现。
于是官方开始探索更好的方案,试图为每个场景自动选择最快的途径,比如为宣传页面静态输出,为介绍页面做动态服务端呈现。
从 Next.js 9 开始,用户无需在完全服务器呈现或静态导出其应用之间做二选一了。新版可以在不同页面上使用不同的策略。
**自动部分静态导出**
新版引入了一种启发式方法来自动确定页面是否可以预呈现为静态 HTML。
程序使用 getInitialProps 判断页面是否有阻止数据的需求来作出选择。
这样一来,Next.js 就可以创建 同时包含服务器呈现和静态生成页面的混合应用。
内置的 Next.js 服务器(next start)和编程 API(app.getRequestHandler())都 透明地 支持这种构建输出。无需配置或特殊处理。
静态生成的页面仍然是反应式的:Next.js 将为应用客户端进行 hydrate 操作,使其具有完整的交互性。
此外,如果页面依赖于 URL 中的查询参数,Next.js 将在 hydrate 操作后更新你的应用程序。
在开发期间静态生成页面时 Next.js 会弹出通知告知开发者。单击通知即可隐藏。
![](https://s4.51cto.com/images/blog/202012/18/fbee3ec6345f31b5f298f771ec9be371.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 静态优化指标
静态生成的页面也将显示在 Next.js 的构建输出中:
![](https://s4.51cto.com/images/blog/202012/18/6fb43223bb8e9fb33b047ecf0520c46c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 构建输出类型指示
### API 路由
许多情况下,开发者在构建 React 应用程序时需要某种后端,用来从数据库检索数据或处理用户提供的数据(例如联系表单)。
官方调研发现许多需要后端的用户使用自定义服务器构建了他们的 API,结果遇到了很多问题。例如,Next.js 不编译自定义服务器代码,这意味着开发者无法使用导入 / 导出或 TypeScript。
因此许多用户最后在自定义服务器之上实现了他们自己的自定义编译管道。虽然这解决了他们的需求,但也容易出现许多陷阱:例如,树抖动(tree shaking)配置不正确时会在整个应用程序中禁用。
现在 Next.js 9 引入了 API 路由,为开发者构建后端带来了最佳体验。
要开始使用 API路由,请在 pages/ 目录中创建名为 api/ 的目录。
此目录中的任何文件都将自动映射到 /api/<你的路径>,就像其他页面文件映射到路由一样。
例如,pages/api/contact.js 将映射到 /api/contact。
注意:API 路由也支持动态路由。
pages/api/ 目录中的所有文件都导出一个请求处理函数,而不是 React 组件:

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

* req 指的是扩展 http.IncomingMessage 的 NextApiRequest。
* res 指的是扩展 http.ServerResponse 的 NextApiResponse。
通常来说,API 端点接收一些传入数据,例如查询字符串、请求正文或 cookie,并使用其他数据响应。
官方调研发现,许多情况下用户没有直接使用 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 将自动预取出现在视口内(in viewport)的<Link>组件。
此功能可以加快跳转到新页面的速度,从而提高应用程序的响应速度。
Next.js 使用 Intersection Observer 预取后台必需的资源 : https://www.w3.org/TR/resource-hints/#prefetch
这些请求优先级较低,并且产生 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 Benz 为 AMP Optimizer 带来的改进: https://github.com/ampproject/amp-toolbox/tree/master/packages/optimizer
**typeof window 分支的无效代码清理**
Next.js 9 在服务器和客户端构建期间用适当的值(undefined 或 object)替换 typeof window。此更改使 Next.js 可以自动从生产构建的应用程序中删除无效代码。
如果用户在 getInitialProps 或应用程序的其他部分中写了纯服务端代码,那么就能注意到客户端包变小了。
### 改善开发体验
**编译指示器**
在之前的版本中,开发者只能查看开发者控制台才能知道热代码替换即将执行(并且 Next.js 编译工具链正在工作)。
然而很多时候,人们正在查看生成的呈现,很难知道 Next.js 是否仍在编译。例如,开发者可能正在对页面上的样式做些小改动,结果没法第一时间知道它们是否已更新过。
官方为此创建了一个 RFC/“第一个好问题” 的页面,以探讨这个问题的可行解决方案: https://github.com/zeit/next.js/issues/4626
官方收到了许多设计师和工程师在 RFC 上的反馈。其中 Rafael Almeida 借此机会与官方团队合作,实现了一个全新的指标,并引入了 Next.js 9。
现在每当 Next.js 执行编译工作时,开发者会看到页面右下角出现一个小三角形。
![](https://s4.51cto.com/images/blog/202012/18/1ad4280a6bcedbc81f1d33c2a68cf290.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 编译指标
**控制台输出**
传统上,开发者在开发中进行更改时,Next.js 会显示一个编译指示器状态,并在开发者作出更改时清屏。
这种行为会导致一些问题。最值得注意的是,它会从应用程序代码中清除控制台输出,例如,当你将 console.log 添加到组件时就会这样。使用外部工具将日志输出拼接在一起时也会清除控制台输出,如 Now CLI 或 docker-compose。
从 Next.js 9 开始日志输出跳跃变少,也不再清屏。这样可以提供更好的体验,因为终端窗口将显示更多相关信息,闪烁也更少了,同时 Next.js 能更好地与你正在使用的工具集成。
![](https://s4.51cto.com/images/blog/202012/18/4bf95854c459906e63862f2265ec957b.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 开发控制台输出
这里特别感谢 Justin Chase 的贡献: https://github.com/justinmchase
**构建输出统计**
使用 next build 构建生产应用时,它将为你提供所有已构建页面的详细视图。
每个页面都会自动收到一些统计信息
最显眼的是包大小。随着应用程序的增长,你的 JavaScript 包也会变大,这个构建时指示可以告诉开发者生产包变大了多少。将来的版本中开发者还可以为生产构建失败的页面设置性能预算: https://addyosmani.com/blog/performance-budgets/
![](https://s4.51cto.com/images/blog/202012/18/0f4e5560817e612d3f25ab3f42c081d4.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 构建出的页面大小
除了包大小,新版还展示了每个页面中使用了多少个项目组件和 node_modules 组件,以帮助开发者了解页面的复杂度。
![](https://s4.51cto.com/images/blog/202012/18/2a48f24aab77bc1a577231a2c74e9ad1.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js 页面包计数
每个页面还有一个指示,告诉开发者它是静态优化还是服务端呈现,因为每个页面的行为都可能不一样。
![](https://s4.51cto.com/images/blog/202012/18/af409647f19a8da063a693f66f60c1d5.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
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 的用法转换为新的配置对象。更多信息可以参阅 升级指南: https://github.com/zeit/next.js/blob/canary/UPGRADING.md
**代码库改进**
新版对一些工具做了一些更改以提供更好的体验。
如前所述,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 上运行这个测试套件。
**谷歌 Chrome 协作**
谷歌 Chrome 团队一直在通过提交 RFC 和 pull request 来改进 Next.js。
双方合作的目标是大规模性能提升,重点是包大小、启动和 hydrate 时间。
例如,这些更改将改善小型网站的体验,也可以改善 Hulu、Twitch 和 Deliveroo 等大型应用程序的体验。
** 模块 /Nomodule**
合作的第一项重点任务是将新版 JavaScript 代码发布到支持新版 JavaScript 的浏览器上。
例如,当前 Next.js 必须为 async/await 语法提供 polyfill,因为代码可能在不支持 async/await 的浏览器中执行。
![](https://s4.51cto.com/images/blog/202012/18/d37beb238f561a62b6a3025cd448a0d1.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js Module/Nomodule 合作 RFC
为了在保持旧浏览器中正常运行应用的前提下向支持 JS 的浏览器发送新版 JavaScript 代码,Next.js 将使用 module/nomodule 模式。这个模式提供了一种可靠的机制,可以将新版 JavaScript 代码提供给现代浏览器,同时仍允许旧版浏览器回退到 polyfilled ES5。
此处 有 Next.js 中的 module/nomodule 的 RFC: https://github.com/zeit/next.js/issues/7563
** 改进包拆分**
Next.js 中当前的包拆分策略使用基于比率的启发式算法,用于在单个“公共”块中包含模块。由于只有一个包的时候粒度非常小,因此会下载不必要的代码(因为公共块可能包含特定路由实际上不需要的代码),或者代码复制到了多个页面包中。
![](https://s4.51cto.com/images/blog/202012/18/1f78d1bc2b3fa65c19abd3a2155d7746.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
Next.js Chunking Collaboration RFC
此处有改进包拆分的 RFC: https://github.com/zeit/next.js/issues/7631
** 其他改进**
Chrome 团队还在对 Next.js 进行许多其他优化和更改。这些 RFC 将很快分享给开发者。
这些 RFC 和 pull request 标记为“协作”,以便在 Next.js 问题跟踪器中轻松找到它们: https://github.com/zeit/next.js/labels/Type%3A%20Collaboration
### 社区
Next.js 社区在继续成长。此版本有超过 65 位 pull request 作者提供核心改进或示例。
现在官方提供了 200 多个关于如何将 Next.js 与不同库和技术集成的示例,包括大多数 css-in-js 和数据获取库。
* 已经有超过 720 名贡献者提交了至少 1 次贡献。
* 在 GitHub 上,该项目已经得到了 38,600 星。
* 自第一个版本发布以来已提交了超过 3,400 个 pull request,自上次主要版本发布以来有超过 800 个。
* 自上次主要版本发布以来,Spectrum.chat/next-js 上的 Next.js 社区注册成员多了一倍,现在超过 8,600 人。
感谢社区以及帮助开发此版本的所有外部反馈和贡献。
英文原文: https://nextjs.org/blog/next-9#built-in-zero-config-typescript-support