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
在极少数情况下,您的代码库可能需要更改。 有关更多信息,请参阅升级指南。
自上次发布以来,我们很高兴看到像 IGN、 Bang & Olufsen、 Intercom、 Buffer 和 Ferrari 等公司使用 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 也会创建一个具有合理默认值的默认文件。 此文件允许在像 Visual Studio Code 这样的编辑器中进行集成的类型检查。
集成类型检查
Next.js 在开发和生产构建中都会为您处理类型检查。
在开发过程中,Next.js 将在保存文件后向您显示类型错误。 类型检查在后台进行,使您可以立即在浏览器中与更新后的应用程序进行交互。 类型错误将在可用时传播到浏览器。
如果存在类型错误,Next.js 也会自动使生产构建失败(即 next build
)。 这有助于防止将损坏的代码发布到生产环境。

Next.js 核心代码使用 TypeScript 编写
在过去的几个月里,我们已将大部分代码库迁移到 TypeScript,这不仅提高了我们的代码质量,还使我们能够为所有核心模块提供类型定义。
例如,当您导入 next/link
时,支持 TypeScript 的编辑器将显示允许的属性以及它们接受的值。

动态路由段
动态路由(也称为 URL Slug 或美观/简洁 URL)是 Next.js 在 2.5 年前发布后,GitHub 上最早的功能请求之一!
这个问题在 Next.js 2.0 中通过引入自定义服务器 API 以编程方式使用 Next.js 而“解决”了。这使得可以将 Next.js 用作渲染引擎,从而能够进行抽象并将传入的 URL 映射到要渲染的特定页面。
我们与用户进行了交流,并检查了他们的许多应用程序,发现他们中的许多人都有自定义服务器。一种模式开始显现:使用自定义服务器最主要的原因是动态路由。
然而,自定义服务器也有其自身的缺陷:路由是在服务器级别而不是代理级别处理的,它是作为单体部署和扩展的,并且容易出现性能问题。
由于自定义服务器要求整个应用程序在单个实例中可用,因此通常难以部署到解决这些问题的 Serverless 环境中。Serverless 请求在代理层路由,并独立扩展/执行以避免性能瓶颈。
此外,我们相信我们可以提供更好的开发者体验!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-nextjs
,query
对象将是 { 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 Learn 部分 阅读有关此功能的更多信息。
自动静态优化
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 的构建输出中显示

API 路由
在许多情况下,当构建 React 应用程序时,您最终需要某种后端。无论是从数据库检索数据还是处理用户提供的数据(例如,联系表单)。
我们发现,许多需要后端的用户使用自定义服务器构建他们的 API。这样做时,他们遇到了不少问题。例如,Next.js 不编译自定义服务器代码,这意味着您无法使用 import
/ export
或 TypeScript。
因此,许多用户最终在自定义服务器之上实现了他们自己的自定义编译管道。虽然这解决了他们的目标,但它容易出现许多缺陷:例如,配置不正确时,树摇优化将对他们的整个应用程序禁用。
这提出了一个问题:如果我们把 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');
}
req
指的是 NextApiRequest,它扩展了 http.IncomingMessageres
指的是 NextApiResponse,它扩展了 http.ServerResponse
通常,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 端点时,代码会自动重新加载,因此无需重启服务器。
生产环境优化
预取视口内的 <Link>
组件
Next.js 9 将在 <Link>
组件出现在视口中时自动预取它们。
此功能通过加快导航到新页面的速度来提高应用程序的响应能力。
Next.js 使用 Intersection Observer API 在后台 预取必要的资源。
这些请求具有低优先级,并让位于 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 Benz 在 AMP Optimizer 上所做的出色工作。
消除 typeof window
分支的死代码
Next.js 9 在服务器和客户端构建期间,将 typeof window
替换为其适当的值(undefined
或 object
)。 此更改允许 Next.js 自动从您的生产构建应用程序中删除死代码。
如果用户在 getInitialProps
或应用程序的其他部分中有仅限服务器端的代码,则应该会看到他们的客户端 bundle 大小减小。
开发者体验改进
编译指示器
在版本 9 之前,要知道热代码替换即将发生(以及 Next.js 编译器工具链正在工作)的唯一方法是查看开发者控制台。
然而,很多时候人们都在查看最终的渲染结果,因此很难知道 Next.js 是否仍在进行编译工作。 例如,您可能正在对页面上的样式进行细微的更改,而您不会立即知道它们是否已更新。
因此,我们创建了一个 RFC / “好的入门问题” 来讨论指示工作正在进行的潜在解决方案。
我们收到了来自许多设计师和工程师对 RFC 的反馈,例如他们偏好的内容以及指示器设计的潜在方向。
Rafael Almeida 借此机会与我们的团队合作,并实现了一个全新的指示器,该指示器现在在 Next.js 9 中默认可用。
每当 Next.js 正在进行编译工作时,您都会在页面右下角看到一个小三角形出现!
控制台输出
传统上,在开发中进行更改时,Next.js 会显示一个编译指示器状态,其中加载状态栏会填充,并在您进行更改时不断清除屏幕。
这种行为会导致一些问题。 最值得注意的是,它会清除来自您的应用程序代码的控制台输出,例如当您将 console.log
添加到您的组件时。 而且在使用外部工具(如 Vercel CLI 或 docker-compose
)将日志输出拼接在一起时也是如此。
从 Next.js 9 开始,日志输出跳动更少,并且不再清除屏幕。 这提供了更好的整体体验,因为您的终端窗口将具有更多相关信息,并且闪烁更少,同时 Next.js 将更好地与您可能已经使用的工具集成。
特别感谢 Justin Chase 在输出清除方面进行的合作。
构建输出统计信息
使用 next build
为生产环境构建您的应用程序时,它现在将为您提供已构建的所有页面的详细视图。
每个页面都会自动收到一些统计信息。
最突出的是 bundle 大小。 随着您的应用程序增长,您的 JavaScript bundle 也会增长,此构建时指示将帮助您指示生产 bundle 的增长。 未来,您还可以为页面设置 性能预算,这将导致生产构建失败。

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

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

每个页面的配置对象
现在每个页面都可以导出一个配置对象。 最初,此配置允许您选择加入 AMP,但在未来,您将能够配置更多页面特定的选项。
export const config = { amp: true };
export default function AboutPage(props) {
return <h3>My AMP About Page!</h3>;
}
要选择加入混合 AMP 渲染,您可以使用值 'hybrid'
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 结合使用,以在 headless Chrome 中进行测试。 然而,随着时间的推移,其他浏览器,尤其是像 Internet Explorer 11 这样的旧浏览器中会出现某些问题。
因为我们使用了 Selenium,所以我们能够在多个浏览器上自动运行我们的测试。
截至目前,我们正在 Chrome、Firefox、Safari 和 Internet Explorer 11 上运行我们的测试套件。
Google Chrome 团队合作
Google Chrome 团队一直在通过贡献 RFC 和 pull-requests 来改进 Next.js。
此合作的目标是大规模的性能改进,重点是 bundle 大小、启动和 hydration 时间。
例如,这些更改将改善小型网站的体验,但也改善像 Hulu、Twitch 和 Deliveroo 这样的大型应用程序的体验。
模块 / Nomodule
首要关注的领域是将现代 JavaScript 运送到支持现代 JavaScript 的浏览器。
例如,目前 Next.js 必须为 async
/await
语法提供 polyfill,因为代码可能在不支持 async
/await
的浏览器中执行,这会导致代码崩溃。

为了避免旧版浏览器出现兼容性问题,同时仍向支持现代 JavaScript 的浏览器发送现代 JavaScript,Next.js 将使用 module/nomodule 模式。 module/nomodule 模式为现代浏览器提供现代 JavaScript,同时仍然允许旧版浏览器回退到 polyfilled ES5,提供了一种可靠的机制。
关于 Next.js 中 module/nomodule 的 RFC 可以在这里找到。
改进的包拆分
目前 Next.js 中的包拆分策略是基于一种基于比率的启发式方法,用于将模块包含在单个 “commons” chunk 中。 由于只有一个 bundle,因此粒度非常低,代码要么被不必要地下载(因为 commons chunk 可能包含特定路由实际上不需要的代码),要么代码在多个页面 bundle 中重复。

关于改进的包拆分的 RFC 可以在这里找到。
其他改进
Chrome 团队也在进行许多其他优化和更改,这些将改进 Next.js。 这些的 RFC 将很快分享。
这些 RFC 和 pull request 都被标记为 “Collaboration”,以便可以在 Next.js issue tracker 中轻松找到它们。
社区
我们很高兴看到 Next.js 社区的持续增长。
此版本有超过 65 位 pull request 作者贡献了核心改进或示例。
说到示例,我们现在提供了超过 200 个关于如何将 Next.js 与不同库和技术集成的示例! 包括大多数 css-in-js 和数据获取库。
- 我们有超过 720 位贡献者 至少提交了 1 次 commit。
- 在 GitHub 上,该项目已被 star 超过 38,600 次。
- 自首次发布以来,已提交了超过 3,400 个 pull request,其中自上次主要版本以来就超过了 800 个!
自上次主要版本以来,Next.js 社区规模翻了一番,拥有超过 8,600 名成员。 加入我们!
我们感谢我们的社区以及所有帮助塑造此版本的外部反馈和贡献。