2019年7月8日,星期一
Next.js 9
发布者经过70个金丝雀版本发布后,我们很高兴推出Next.js 9,其中包含:
- 内置零配置TypeScript支持:得益于自动TypeScript支持和集成类型检查,您可以更自信地构建应用程序。
- 基于文件系统的动态路由:通过文件系统表达复杂的应用程序路由需求,无需自定义服务器。
- 自动静态优化:默认情况下,利用服务器端渲染和静态预渲染创建超快网站,而不会影响功能。
- API 路由:快速构建后端应用程序端点,利用热重载和统一的构建管道。
- 更多生产优化:由于视口内预取和其他优化,应用程序响应速度比以往任何时候都快。
- 改进的开发体验:不引人注目、易于使用的改进,帮助您发挥最佳开发水平。
一如既往,我们努力确保所有这些优势都向后兼容。对于大多数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所需的类型。
如果尚未存在,Next.js还将创建一个具有合理默认值的tsconfig.json。此文件允许在Visual Studio Code等编辑器中进行集成类型检查。
集成类型检查
Next.js 在开发和生产构建过程中都会为您处理类型检查。
在开发过程中,Next.js 会在保存文件后显示类型错误。类型检查在后台进行,允许您在浏览器中即时与更新后的应用程序交互。类型错误会在可用时传播到浏览器。
如果存在类型错误,Next.js 也会自动导致生产构建(即 next build)失败。这有助于防止将损坏的代码发布到生产环境。

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

动态路由段
动态路由(也称为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-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 学习部分中阅读有关此功能的更多信息。
自动静态优化
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。
因此,许多用户最终在自定义服务器之上实现了自己的自定义编译管道。虽然这解决了他们的目标,但它容易出现许多陷阱:例如,如果配置不正确,其整个应用程序的摇树(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');
}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 在后台 预取必要的资源。
这些请求具有低优先级,并让位于 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上令人难以置信的工作。
typeof window 分支的死代码消除
Next.js 9 在服务器和客户端构建期间将 typeof window 替换为其适当的值(undefined 或 object)。此更改允许 Next.js 自动从您的生产构建应用程序中删除死代码。
如果用户在 getInitialProps 或其应用程序的其他部分有仅限服务器的代码,则应看到其客户端捆绑包大小减小。
开发人员体验改进
编译指示器
在 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 构建您的应用程序以投入生产时,现在将为您提供所有已构建页面的详细视图。
每个页面都会自动获得一些统计数据。
最突出的是捆绑包大小。随着应用程序的增长,您的 JavaScript 捆绑包也会增长,这种构建时指示将帮助您指示生产捆绑包的增长。将来,您还可以为将导致生产构建失败的页面设置性能预算。

除了捆绑包大小,我们还显示每个页面中使用了多少项目组件和 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 结合使用在无头 Chrome 中进行测试。然而,随着时间的推移,在其他浏览器中会出现某些问题,尤其是 Internet Explorer 11 等旧浏览器。
因为我们使用 Selenium,所以我们能够自动在多个浏览器上运行测试。
目前,我们正在 Chrome、Firefox、Safari 和 Internet Explorer 11 上运行我们的测试套件。
Google Chrome 合作
Google Chrome 团队一直通过贡献 RFC 和拉取请求来改进 Next.js。
此次合作的目标是实现大规模的性能改进,主要集中在捆绑包大小、启动和水合时间。
例如,这些更改将改善小型网站的体验,同时也将改善像Hulu、Twitch和Deliveroo这样的大型应用程序的体验。
模块 / 无模块
第一个重点是向支持现代 JavaScript 的浏览器提供现代 JavaScript。
例如,目前 Next.js 必须为 async/await 语法提供 polyfill,因为代码可能在不支持 async/await 的浏览器中执行,这会导致代码中断。

为了避免破坏旧版浏览器,同时仍向支持现代 JavaScript 的浏览器发送现代 JavaScript,Next.js 将利用模块/非模块模式。模块/非模块模式提供了一种可靠的机制,可以将现代 JavaScript 提供给现代浏览器,同时仍允许旧版浏览器回退到 polyfilled ES5。
Next.js 中模块/非模块的 RFC 可以在这里找到。
改进的捆绑包拆分
Next.js 中当前的捆绑包拆分策略基于一种比率启发式算法,用于将模块包含在单个“公共”块中。由于只有一个捆绑包,粒度非常小,代码要么不必要地下载(因为公共块可能包含特定路由不需要的代码),要么代码在多个页面捆绑包中重复。

改进的捆绑包拆分 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 名成员**。加入我们吧!
我们感谢社区以及所有帮助塑造此版本的外部反馈和贡献。