经过 70 个 canary 版本迭代后,我们很高兴推出 Next.js 9,主要特性包括:
- 内置零配置 TypeScript 支持:通过自动 TypeScript 支持和集成类型检查,让您更有信心地构建应用。
- 基于文件系统的动态路由:无需自定义服务器即可通过文件系统表达复杂的应用路由需求。
- 自动静态优化:默认创建利用服务端渲染 (SSR) 和静态预渲染的超快网站,同时不牺牲功能。
- API 路由:利用热重载和统一的构建管道快速构建后端应用端点。
- 更多生产环境优化:得益于视口内预取等优化,应用响应速度比以往更快。
- 改进的开发体验:无干扰的易用性改进,助您高效开发。
一如既往,我们努力确保所有这些改进都向后兼容。对于大多数 Next.js 应用,您只需运行:
极少数情况下您的代码库可能需要调整。更多信息请参阅升级指南。
自上次发布以来,我们很高兴看到 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 9 自动化 TypeScript 设置
集成类型检查
Next.js 在开发和生产构建时都会为您处理类型检查。
在开发过程中,Next.js 会在保存文件后显示类型错误。类型检查在后台进行,让您可以立即在浏览器中与更新后的应用交互。类型错误会在可用时传播到浏览器。
Next.js 9 开发环境类型检查
如果存在类型错误,Next.js 还会自动使生产构建(即 next build
)失败。这有助于防止将有问题的代码发布到生产环境。
Next.js 9 生产环境类型检查
Next.js 核心使用 TypeScript 编写
过去几个月,我们已将大部分代码库迁移到 TypeScript,这不仅增强了代码质量,还让我们能为所有核心模块提供类型定义。
例如,当您导入 next/link
时,支持 TypeScript 的编辑器会显示允许的属性及其可接受的值。
Next.js 核心类型
动态路由段
动态路由(也称为 URL 别名或美观/简洁 URL)是 Next.js 2.5 年前发布后在 GitHub 上的首批功能请求之一!
Next.js 2.0 通过引入可编程使用 Next.js 的自定义服务器 API "解决"了这个问题。这允许将 Next.js 用作渲染引擎,支持抽象并将传入 URL 映射到特定页面进行渲染。
我们与用户交流并检查了许多应用,发现许多应用都有自定义服务器。一个模式浮现:使用自定义服务器的最主要原因是动态路由。
然而,自定义服务器有其缺点:路由在服务器级别而非代理层处理,作为单体部署和扩展,容易出现性能问题。
由于自定义服务器要求整个应用在一个实例中可用,通常难以部署到解决这些问题的无服务器 (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' }
:
还支持多个动态 URL 段!
[param]
语法支持目录名和文件名,意味着以下示例有效:
您可以在 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 静态优化指示器
静态生成的页面也会显示在 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 组件:
req
指代 NextApiRequest,它扩展了 http.IncomingMessageres
指代 NextApiResponse,它扩展了 http.ServerResponse
通常 API 端点接收一些传入数据,例如查询字符串、请求体或 cookie,并响应其他数据。
在研究将 API 路由支持添加到 Next.js 时,我们注意到在许多情况下用户不直接使用 Node.js 请求和响应对象。相反,他们使用服务器库(如 Express)提供的抽象。
这样做的原因是在许多情况下传入数据是某种需要首先解析才能使用的文本。因此这些特定的服务器库帮助减轻手动解析数据的负担,通常通过中间件。最常用的中间件提供查询字符串、请求体和 cookie 解析,但仍需要一些设置才能开始使用。
Next.js 中的 API 路由默认提供这些中间件,让您可以立即高效创建 API 端点:
除了使用传入数据,您的 API 端点通常还会返回数据。通常此响应会是 JSON。Next.js 默认提供 res.json()
以简化数据发送:
在开发过程中对 API 端点进行更改时,代码会自动重新加载,因此无需重启服务器。
生产环境优化
视口内 <Link>
预取
Next.js 9 将在 <Link>
组件出现在视口内时自动预取它们。
此功能通过使导航到新页面更快来提高应用的响应速度。
Next.js 使用 Intersection Observer 来预取后台所需的资源。
这些请求优先级较低,会为 fetch()
或 XHR 请求让路。如果用户启用了数据节省模式,Next.js 将避免自动预取。
您可以通过将 prefetch
属性设置为 false
来为很少访问的页面选择退出此功能:
默认优化的 AMP
Next.js 9 现在默认对 AMP 优先页面和混合 AMP 页面进行优化渲染。
虽然 AMP 页面是可选的,但 Next.js 会自动优化其输出。这些优化可使渲染速度提升高达 50%!
这一改进得益于 Sebastian Benz 在 AMP 优化器 上的卓越工作。
typeof window
分支的无效代码消除
Next.js 9 在服务端和客户端构建时会用适当的值(undefined
或 object
)替换 typeof window
。这一改进使得 Next.js 能自动从生产构建的应用中移除无效代码。
如果用户在 getInitialProps
或其他应用部分包含仅限服务端的代码,将会看到客户端包体积的减小。
开发者体验改进
编译指示器
在 9 之前的版本中,了解热代码替换即将发生(以及 Next.js 编译工具链正在工作)的唯一方式是查看开发者控制台。
然而很多时候开发者关注的是渲染结果,这使得难以判断 Next.js 是否仍在进行编译工作。例如,当对页面样式进行细微调整时,可能无法立即察觉更新是否生效。
为此,我们创建了一个 RFC/"good first issue" 来讨论指示工作状态的潜在解决方案。
我们收到了来自许多设计师和工程师的反馈,包括他们的偏好和指示器设计的潜在方向。
Rafael Almeida 抓住这个机会与我们团队合作,实现了一个全新的指示器,现已在 Next.js 9 中默认启用。
当 Next.js 进行编译工作时,页面右下角会出现一个小三角形!
Next.js 编译指示器
控制台输出
传统上,在开发过程中进行更改时,Next.js 会显示一个带有加载状态条的编译指示器,并会在更改时持续清屏。
这种行为会导致一些问题。最明显的是它会清除应用代码的控制台输出,例如在组件中添加 console.log
时。同时也会影响使用外部工具(如 Vercel CLI 或 docker-compose
)拼接日志输出的情况。
从 Next.js 9 开始,日志输出跳动减少且不再清屏。这提供了更好的整体体验,因为终端窗口将显示更多相关信息且闪烁减少,同时 Next.js 能更好地与现有工具集成。
Next.js 开发控制台输出
特别感谢 Justin Chase 在输出清除方面的协作。
构建输出统计
使用 next build
为生产环境构建应用时,现在会提供所有已构建页面的详细视图。
每个页面都会自动显示一些统计信息。
最突出的是包体积。随着应用增长,JavaScript 包也会增大,这一构建时指标有助于监控生产包的体积增长。未来还将支持为页面设置性能预算,超出预算将导致生产构建失败。
Next.js 构建页面大小
除了包体积,我们还显示每个页面使用了多少项目组件和 node_modules
组件,以反映页面复杂度。
Next.js 页面包数量
每个页面还会标明是静态优化还是服务端渲染,因为不同页面的行为可能不同。
Next.js 构建页面类型
页面级配置对象
现在每个页面都可以导出一个配置对象。最初该配置允许选择启用 AMP,未来还将支持更多页面特定选项。
要启用混合 AMP 渲染,可以使用值 'hybrid'
:
withAmp
高阶组件已被移除,推荐使用这一新配置。
我们提供了 代码修改工具 来自动将 withAmp
转换为新的配置对象。更多信息请参阅 升级指南。
代码库改进
我们最近对工具链进行了一些改进,以提升贡献代码库时的体验,并确保随着代码库增长保持稳定性。
如 TypeScript 部分所述,Next.js 核心现已用 TypeScript 编写,并自动为 Next.js 应用生成类型定义。这不仅对使用 Next.js 构建的应用有用,对核心代码库开发也很有帮助,因为会自动获得类型错误提示和自动补全。
Next.js 已有超过 50 个 Next.js 应用组成的大型集成测试套件,确保新版本发布时升级过程顺畅,因为之前可用的功能都通过了相同的测试套件验证。
我们的测试大多是集成测试,因为它们能模拟开发者实际使用 Next.js 的场景。例如我们有测试模拟对 Next.js 应用进行更改以验证热模块替换是否正常工作。
我们的集成测试主要基于 Selenium WebDriver,结合 ChromeDriver 在无头 Chrome 中运行测试。但随着时间推移,其他浏览器(尤其是 IE11 等旧版浏览器)会出现一些问题。
由于使用 Selenium,我们能在多个浏览器上自动运行测试。
目前我们的测试套件支持 Chrome、Firefox、Safari 和 IE11。
与 Google Chrome 团队的合作
Google Chrome 团队一直通过贡献 RFC 和拉取请求来改进 Next.js。
这一合作的目标是大规模性能优化,重点关注包体积、启动时间和水合时间。
例如这些改进将提升小型网站体验,也会优化像 Hulu、Twitch 和 Deliveroo 这样的大型应用。
Module / Nomodule
第一个重点领域是向支持现代 JavaScript 的浏览器发送现代 JavaScript。
例如目前 Next.js 需要为 async
/await
语法提供 polyfill,因为代码可能在旧版浏览器中执行,而它们不支持 async
/await
,会导致错误。
Next.js Module/Nomodule 协作 RFC
为避免破坏旧版浏览器同时仍向现代浏览器发送现代 JavaScript,Next.js 将采用 module/nomodule 模式。该模式提供了一种可靠机制,既能向现代浏览器提供现代 JavaScript,又能让旧版浏览器回退到带 polyfill 的 ES5。
Next.js 中 module/nomodule 的 RFC 可在此查看。
改进的包拆分
当前 Next.js 的包拆分策略基于一种启发式比例,将模块包含在单个 "commons" 块中。由于只有一个包,粒度很小,代码要么被不必要地下载(因为 commons 块可能包含特定路由不需要的代码),要么在多个页面包中重复。
Next.js 分包协作 RFC
改进包拆分的 RFC 可在此查看。
其他改进
Chrome 团队还在进行许多其他优化和改进 Next.js 的工作。这些 RFC 将很快分享。
这些 RFC 和拉取请求都标记为 "Collaboration",便于在 Next.js 问题跟踪器中查找。
社区
我们很高兴看到 Next.js 社区的持续壮大。
此版本有超过 65 位拉取请求作者贡献了核心改进或示例。
说到示例,我们现在提供了超过 200 个示例,展示如何将 Next.js 与不同库和技术集成!包括大多数 CSS-in-JS 和数据获取库。
- 我们有超过 720 位贡献者 提交了至少 1 次提交。
- 在 GitHub 上,项目已获得超过 38,600 次星标。
- 自首次发布以来提交了超过 3,400 个拉取请求,其中自上一个主要版本以来有 超过 800 个!
Next.js 社区自上一个主要版本以来已翻倍,拥有超过 8,600 名成员。加入我们!
我们感谢社区以及所有外部反馈和贡献,这些帮助塑造了此版本。