【译】Next.js 12

3,728 阅读12分钟

时值字节跳动推出 modern.js 被广大国内 FE 群嘲之际,nextjs 迎来了自己的第十二个 major 版本。官方说这是 nextjs 有史以来最大的一次发版。既然这么地里程碑,那么发布 blog 更值得翻译和留作纪念了。全文意译,如有误导,请放弃阅读。

正文

前言

正如我们在 Next.js Conf 上宣布的那样,Next.js 12 是我们有史以来最大的版本:

  • Rust 编译器:相比原先,约 3 倍的 Fast Refresh 和约 5 倍的构建速度提升
  • 中间件(beta):通过编写代码而不是配置来在 Next.js 中实现更强的灵活性
  • React 18 支持:现在原生 Next.js API 已经支持 React 18 的特性,包括 Suspense
  • <Image /> AVIF 支持:可选地缩小 20% 的图像
  • Bot-aware ISR Fallback:优化 SEO,以便更好支持网络爬虫
  • 原生 ES 模块支持:跟标准化模块系统对齐
  • URL 导入(alpha):可以通过 URL 来导入 npm 包,无需安装
  • React Server Components (alpha):立即试用,包括 SSR streaming 特性

今天你就可以通过运行 npm i next@latest 来尽情地享用。

rust compiler - 更快的 fast refresh 和构建速度

我们想让每个 Next.js 应用程序在生产环境中构建得更快,并在本地开发中获得即时反馈。 Next.js 12 包含一个全新的 Rust 编译器,它利用了原生语言的编译能力。

swc.png

我们的 Rust 编译器建立在 SWC 之上(SWC 是面向未来的,旨在打造高效构建工具的开放平台)。我们优化了打包和编译,本地开发环境下,fast refresh 速度提高了约 3 倍,生产环境下构建速度提高了约 5 倍。其他改进和功能包括:

  • 大规模代码库的构建速度提升:我们已经使用世界上一些最大的Next.js Codebase 验证了Rust编译器的编译速度。
  • 提高了性能指标的可观察性:Next.js 在编译的时候会将编译所经历的时间节点,包括编译的模块数量和文件名称打印到浏览器和服务器的控制台,让开发者能够更加直观地看到编译性能上的提升。
  • 底层 WebPack 改进:我们对 webpack 进行了多种改进,包括优化 fast refresh 并使按需加载(on-demand entries)更加可靠。

使用 Rust 编译比 Babel 更快17倍。在 nextjs v12 默认是使用 rust 编译器来对 javaScript和 typescript 文件进行编译。这意味着我们必须将其他的 babel 的转换能力移植到 rust 中来。在nextjs v12中,内置了一个用 rust 写的,全新的 css 解析器,用于实现对 styled-jsx的转译。

新的 Rust编译器向后兼容。如果你当前的项目已经有一份 Babel 文件 ,那么 nextjs 会回退为使用 babel 来编译。我们尝试复用你的 babel 配置文件,但是使用 rust 编译器来对当前流行的类库,比如styled-components, emotion, relay进行编译。如果你有自定义的 babel 配置,麻烦贡献一下你的配置文件,以供测试

Rust 编译器包含了代码压缩的功能,这比Terser快7倍。当前阶段,rust 编译器的代码压缩是可选的(也即是说你可以不用),直到它彻底被验证是可行的,我们才会取消这个可选配置。这么做,是因为它要取代的是存在了多年的众多基础设施。

// next.config.js

module.exports = {
  swcMinify: true
}

除了录用 DongYoon Kang(SWC的作者)和Maia Teegarden(parcel的代码贡献者)之外,我们会继续对 rust 的社区进行持续投入。如果你有 rust 的开发经验的话,欢迎加入到我们的团队当中来

有关更多信息,请观看我们的 Next.js Conf 大会里面给出的 demo

引入 middleware

中间件使得你可以自由编码而不是使用配置来达成某个目的。这在Next.js中为您提供了完整的灵活性,因为您可以在请求完成之前运行代码。基于用户的传入请求,您可以通过重写,重定向,添加标题甚至 流式HTML 来修改响应。

middleware.png

中间件可以实现一组页面之间的逻辑代码共享,包括:

  • 身份验证
  • 机器人保护
  • 重定向和重写
  • 处理不支持的浏览器
  • 功能标志和 A / B测试
  • 服务器端分析
  • 高级 i18n 路由需求
  • 打日志
  • ......

中间件使用一个严格的 runtime 来支持标准Web API,如fetch。运行 next start就可以开箱即用,通用开箱即用的还有 Vercel 这样的 edge 平台里面的 Edge Functions

要在Next.js中使用中间件,您可以创建文件 pages/ _MiddleWare.js。在此示例中,我们使用标准 Web API响应(MDN):

// pages/_middleware.js

export function middleware(req, ev) {
  return new Response('Hello, world!')
}

有关更多信息,请观看 Next.js Conf大会上给出的 demo查看文档

为引入 React 18 做准备

React 18 将添加 suspense,自动批量更新,startTransition API 和 为了在服务端渲染环境下支持React.lazy的新的streaming API 等功能。

我们一直与 Facebook的 React团队密切合作,为在 Next.js 中引入稳定版的 React 18 做准备。通过使用试验表标志位,我们可以在今天的 nextjs 12 中尝试这些功能。

$ npm install react@alpha react-dom@alpha

Server-Side Streaming

React 18中的并发特性包括内置支持 server-side Suspense 和流式 SSR。这允许您使用HTTP流来渲染页面。这是Next.js 12中的一个实验功能,但一旦启用,SSR将使用与中间件相同的严格 runtime。

要启用,请使用实验标志位concurrentFeatures: true:

// next.config.js
module.exports = {
  experimental: {
    concurrentFeatures: true
  }
}

React Server component

React Server组件允许我们在服务器上渲染所有内容,包括组件本身。这与预生成 HTML 的普通 SSR 是不同的。有了服务器组件,我们可以消除那些不必要的客户端 javascript 代码,使的页面渲染得更快。这改善了应用程序的用户体验,同时让你收获了 SSR 的精华部分和客户端的丰富的交互性。

// next.config.js
module.exports = {
  experimental: {
    concurrentFeatures: true,
    serverComponents: true
  }
}

Next.js 12 允许你在组件级别进行数据请求,一切都表达为 JSX。通过使用React Server component,我们让事情变得简单。有了它之后,不再需要诸如GetServerSidePropsGetStaticProps等特殊函数。这与你在之前 react component 中获取和存放数据的React Hooks模型对齐。

您可以通过将Next.js项目 page 目录下的某个文件重命名为 .server.js 来创建 react server component,然后将 client component 直接导入进来使用。这些客户端组件将会发生 hydrate 因而变得可交互,因此您可以添加像 Upvotes 这样的功能。

我们目前正在开发服务器端 suspense局部 hydrate流式渲染,并将在未来的博客文章中分享我们的进展。

特别感谢我们的合作伙伴Kara EricksonGoogle Aurora 团队的Gerald Monaco在 React 18 和 react server component 上的工作付出。

有关更多信息,请看我们在 Next.js Conf大会上给出的的演示查看文档

原生 ES 模块支持和 URL import

ES 模块为 JavaScript 带来了官方的、标准化的模块系统。所有主要浏览器以及 Node.js 都支持它们。

该标准通过支持更小的 package 大小和 JavaScript bundle来推动 Web 生态系统向前发展,最终带来更好的用户体验。随着 JavaScript 生态系统从 Common JS(旧标准)过渡到 ES 模块,我们致力于帮助开发人员逐步采用这些改进,而无需进行不必要的破坏性更改。

Next.js 11.1 开始,我们添加了对 ES 模块优先于 CommonJS 模块的实验性支持。在 Next.js 12 中,这是现在的默认设置。当然,我么仍然支持导入仅提供 CommonJS 打包的 NPM 模块。

URL import

Next.js 12 包含对通过 URL 导入 ES 模块的实验性支持,不需要安装或单独的构建步骤。

URL import 允许您直接通过 URL 使用任何包。这使 Next.js 能够像本地依赖一样处理远程 HTTP(S) 资源。

如果检测到 URL 导入,Next.js 将生成一个 next.lock 文件来跟踪远程资源。 URL 导入将在本地创建缓存,以确保您仍然可以离线工作。 Next.js 支持客户端和服务器的 URL import。

如果想要尝鲜的话,那么你可以在 next.config.js 配置文件中添加相应的允许的 URL 前缀配置:

module.exports = {
  experimental: {
    urlImports: ['https://cdn.skypack.dev']
  }
}

之后,你就可以在你模块中这样导入第三方模块:

import confetti from 'https://cdn.skypack.dev/canvas-confetti'

任何支持托管 ES 模块的 CDN 服务商都是支持的,包括一些 no-code 平台和设计工具(比如:Framer):

有关更多信息,请看我们在 Next.js Conf 大会上给出的的演示查看文档

机器人感知 ISR(Incremental Static Regeneration) 回退

当前,当你设置fallback:true 的时候,那么 ISR 会在特定页面的首次请求,页面还没生成的时候渲染一个 fallback 状态页面。如果你想阻塞页面的渲染,你可以使用 fallback: 'blocking'

在 nextjs 12 中,web crawlers (e.g. search bots)会自动对使用了fallback: true的页面进行服务端的 ISR。虽然,对于非爬虫类的客户端依然是展示之前的 fallback 状态页面,但是对于爬虫客户端而言,这能够阻止它对当前的 fallback 页面进行索引,因为这不是我们(站主)想要的。

支持 AVIF - 更小体积的图片

内置的图像优化 API 现在支持 AVIF 图像,与 WebP 相比,图像小 20%。

与 WebP 相比,AVIF 图像可能需要更长的时间来优化,因此我们使用 next.config.js 中的新 images.formats 字段来启用此功能:

module.exports = {
  images: {
    formats: ['image/avif', 'image/webp']
  }
}

此格式列表用于跟请求的 Accept 标头来确定需要优化的图像格式。由于 AVIF 是列表中的第一个,如果浏览器支持 AVIF,那么它将会被优先使用。否则,如果浏览器支持 WebP,则将提供 WebP。如果两种格式都不支持,则将提供原始图像格式。

打包输入文件的追踪

在 Next.js 8 中,我们引入了 target配置项。这允许通过在构建期间使用 webpack 打包所有依赖,将 Next.js 中的页面作为独立的 JavaScript 包输出。我们很快意识到这并不理想,从而创建了@vercel/nft@vercel/nft 已在 Vercel 平台上的所有部署中使用了 2 年多了。

现在,我们默认将这些优化直接引入 Next.js 框架中,适用于所有部署平台,提供比target配置项有着显着改进的方法。

Next.js 12 使用@vercel/nft 自动跟踪每个页面和 API 路由需要哪些文件,并在next build命令控制台输出旁边打印这些统计信息,允许第三整合方利用 Next.js 自动提供的追踪。

这些变化还优化了使用next start+ Docker 等工具部署的应用程序。通过利用@vercel/nft,我们将来可以使 Next.js 输出独立。无需安装任何依赖项即可运行应用程序,从而大大减少了 Docker 镜像的大小。

@vercel/nft 引入 Next.js 取代了target配置项方法,因此,target配置项在 Next.js 12 中是处于宣告废弃状态的。查看文档以获取更多信息

其他优化

  • 添加自定义的pages/_app.js或者pages/_document.js来替换内置的这两个文件,现在重新启动 next 开发服务器了。
  • 现在整合到 nextjs 12 中的 eslint 支持对特定的文件进行 lint。具体就是在next lint命令你后面追加--file参数。
  • nextjs 12 支持设置自定义的tsconfig.json文件路径。
  • next.config.mjs 配置文件支持写成 ES module。
  • In-flight requests 现在已经跟getStaticProps方法解耦了。
  • 现在使用一个 worker 线程池来检查当前正在运行的静态页面。
  • Fast Refresh 现在使用 WebSocket 连接而不是使用 EventSource 连接。

Breaking Changes

  • 随着我们在 Next.js 11 之后默认使用 webpack 5,我们宣布,在 Next.js 12 中我们已经移除了对 webpack 4 的使用。现在,我们与社区紧密合作,保证大家能够平滑迁移到 webpack 5 上来。
  • next.config.js 中的 target 配置项已经不在需要了。
  • next/image 现在使用 span 作为包裹元素,而不是使用 div
  • 要求的 nodejs 最低版本已经从 12.0.0 上升到 12.22.0了。nodejs v12.22.0 是支持 ES moudle 的第一个版本。

想要了解更过,请查看我们的升级指引

最后的话

五年前,我们向公众发布了 Next.js。我们致力于构建一个零配置的 React 框架,以简化开发者们的开发体验。回首过去,当我们看到如今社区的发展地步和我们能够交付给社区的东西,这一切都是令人难以置信。更美好的未来在等着我们呢,让我们一起不忘初心,继续砥砺前行吧。

Next.js 是 1,800 多名个人开发者、Google 和 Facebook 等行业合作伙伴以及我们的核心团队共同努力的结果,掌声送给他们。

感谢倾力促成本次版本发布的以下人员:@ka2n, @housseindjirdeh, @rojserbest, @lobsterkatie, @thibautsabot, @javivelasco, @sokra, @rishabhpoddar, @kdy1, @huozhi, @georgegach, @ionut-botizan, @paul-creates, @TimBarley, @kimizuy, @devknoll, @matamatanot, @christianvuerings, @pgrodrigues, @mohamedbhy, @AlfonzAlfonz, @kara, @molebox, @angelopoole, @oste, @genetschneider, @jantimon, @kyliau, @mxschmitt, @PhattOZ, @finn-orsini, @kriswuollett, @harryheman, @GustavoEdinger, @AryanBeezadhur, @Blevs, @colevscode, @atcastle, @ijjk, @velocity23, @jonowu, @timneutkens, @whitep4nth3r, @micro-chipset, @TyMick, @padmaia, @arthurdenner, @vitorbal, @zNeb, @jacksonhardaker, @shuding, @kylemh, @Bundy-Mundi, @ctjlewis, @thien-do, @leerob, @Dev-CasperTheGhost, @janicklas-ralph, @rezathematic, @KonstHardy, @fracture91, @lorensr, @Sheraff, @HaNdTriX, @emilio, @oluan, @ddzieduch, @colinclerk, @x4th, @volcareso, @oiva, @sinchang, @scottrepreneur, @smakosh, @catnose99, @adrienharnay, @donsn, @andersonleite, @msp5382, @tim-hanssen, @appsplash99, @alexvilchis, @RobEasthope, @royal, @Perry-Olsson, @well-balanced, @mrmckeb, @buraksakalli, @espipj, @prateekbh, @AleksaC, @eungyeole, and @rgabs