Turborepo

为什么选择 Turbopack?

当我们开始创建 Turbopack 时,我们想要解决一个问题。我们一直在努力提高 Next.js 的速度。我们放弃了一些基于 JS 的工具。Babel,没了。Terser,没了。我们的下一个目标是另一个基于 JS 的工具,webpack。

取代它成为了我们的目标。但是用什么来取代呢?

新一代的本机速度打包器正在兴起,但在评估市场上的打包器后,我们决定构建自己的打包器。为什么?

统一图

像 Next.js 这样的框架之所以如此受欢迎,很大一部分原因是使用当前一代的打包器实现 SSR(以及现在的 RSC)等功能并非易事。您需要为每个输出环境(浏览器、服务器等)创建多个编译器,并管理它们之间的通信,以便它们的包最终正确地拼接在一起。

我们希望从 Next.js 和任何选择使用 Turbopack 的框架中消除这种维护负担。我们还可以通过设计一个可用于为多个环境生成捆绑包的单一统一图来创建更干净、更稳定的实现。

捆绑与原生 ESM

像 Vite 这样的框架使用一种技术,即在开发模式下不捆绑应用程序源代码。相反,它们依赖于浏览器的原生 ES 模块系统。这种方法可以实现令人难以置信的快速更新,因为它们只需要转换单个文件。

我们尝试了这种方法,但在由许多模块组成的大型应用程序中遇到了扩展问题。浏览器中大量的级联网络请求导致启动时间相对较慢。对于浏览器来说,如果它能以尽可能少的网络请求接收到它需要的代码,那么速度会更快——即使在本地服务器上也是如此。

这就是为什么我们决定,像 webpack 一样,我们希望 Turbopack 在开发服务器中捆绑代码。Turbopack 可以更快地完成这项工作,尤其是在大型应用程序中,因为它使用 Rust 编写,并且跳过了仅在生产环境中才需要的优化工作。

增量计算

有两种方法可以使过程更快:减少工作量或并行执行工作。我们知道,如果我们想制造最快的打包器,我们就需要同时利用这两种方法。

我们决定创建一个可重用的 Turbo 构建引擎,类似于 Parcel 的请求管理器和 rustc 的查询系统,用于分布式和增量行为。Turbo 引擎的工作方式类似于函数调度的调度程序,允许跨所有可用核心并行化函数调用。

Turbo 引擎还会缓存它调度的所有函数的结果,这意味着它永远不需要做两次相同的工作。简单来说,**它以最快的速度完成最少的工作**。

延迟捆绑

早期版本的 Next.js 尝试在开发模式下捆绑_整个_ Web 应用程序。我们很快意识到,这种“急切”的方法远非最佳。现代版本的 Next.js 仅捆绑开发服务器请求的页面。例如,如果您转到 localhost:3000,它将仅捆绑 pages/index.jsx 及其导入的模块。

这种更“延迟”的方法(仅在绝对必要时才捆绑资源)是快速开发服务器的关键。原生 ESM 可以毫不费力地处理这个问题——你请求一个模块,该模块又请求其他模块。但是,由于上述原因,我们想要构建一个打包器。

esbuild 没有“延迟”捆绑的概念——除非您专门针对某些入口点,否则它要么全部要么全部放弃。

Turbopack 的开发模式会根据收到的请求构建应用程序导入和导出的最小图,并且仅捆绑必要的最少代码。请在增量计算文档中了解更多信息。

总结

我们想要

  • 支持统一图。这允许框架使用可以针对多个环境的单个编译器。
  • 构建一个打包器。在处理大型应用程序时,打包器的性能优于原生 ESM。
  • 使用增量计算。Turbo 引擎将其引入 Turbopack 架构的核心——最大限度地提高速度并最大限度地减少完成的工作。
  • 优化我们开发服务器的启动时间。为此,我们构建了一个延迟资源图,以仅计算请求的资源。

这就是我们选择构建 Turbopack 的原因。

本页内容