打包
文档
为什么选择 Turbopack?

为什么选择 Turbopack?

当我们着手创建 Turbopack 时,我们希望解决一个问题。我们一直在为 Next.js 寻找速度改进方案。我们迁移了几个基于 JS 的工具。Babel,已移除。Terser,已移除。我们的下一个目标是另一个基于 JS 的工具,Webpack。

用什么来替换它成为了我们的目标。

新一代的原生速度打包器正在出现,但在评估了市场上的打包器后,我们决定自己构建一个。为什么呢?

统一图

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

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

打包 vs 原生 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 的原因。