turbopack ,webpack的官方继任者,快700倍

1,603 阅读4分钟

Turbopack 是针对 JavaScript 和 TypeScript 优化的增量打包器,由 Vercel 的 Webpack 和 Next.js 的创建者用 Rust 编写。

在大型应用程序上,Turbopack 的更新速度比 Vite 快 10 倍,比 Webpack 快 700 倍。对于最大的应用程序,差异变得更加明显,更新速度比 Vite 快 20 倍。

image.png

Turbopack 性能的秘诀是双重的:高度优化的机器代码和低级增量计算引擎,可以缓存到单个函数的级别。而且做到了不重复,一旦 Turbopack 执行了一项任务,它就再也不会这样做了。 我们的团队吸取了 Webpack 10 年的经验教训,结合了 Turborepo 和 Google 的 Bazel 在增量计算方面的创新,并创建了一个可以支持未来几十年计算的架构

让我们深入了解 Turbopack 的内部结构,找出它为何如此之快。

The Turbo engineTurbo 引擎

Turbopack 之所以如此之快,是因为它建立在一个可重用的 Rust 库之上,该库支持称为 Turbo 引擎的增量计算。

函数级缓存

在 Turbo 引擎驱动的程序中,您可以将某些功能标记为“要记住”。当这些函数被调用时,Turbo 引擎会记住它们被调用的内容,以及它们返回的内容。然后它将其保存在内存缓存中。

这是一个简化的示例,说明这在捆绑器中可能是什么样子:

image.png

我们首先在两个文件 api.tssdk.ts 上调用 readFile。然后我们捆绑这些文件,将它们连接在一起,最后得到 fullBundle。所有这些函数调用的结果都保存在缓存中以备后用。

假设我们在开发服务器上运行。您将 sdk.ts文件保存在您的机器上。Turbopack 接收到文件系统事件,并知道它需要重新计算 readFile("sdk.ts"):

image.png

由于 sdk.ts 的结果发生了变化,我们需要再次打包,然后需要再次拼接。 至关重要的是,api.ts 没有改变。我们从缓存中读取它的结果并将其传递给 concat

因此,我们通过不阅读并重新打包来节省时间。

现在想象一下,在一个真正的捆绑器中,有数千个文件要读取和转换要执行。心智模型是一样的。您可以通过记住函数调用的结果而不是重复以前完成的工作来节省大量工作。

按要求编译

Turbo 引擎有助于在您的开发服务器上提供极快的更新,但还有另一个重要指标需要考虑 - 启动时间。您的开发服务器开始运行的速度越快,您开始工作的速度就越快。

应用级编译

2-3 年前的 Next.js 版本会在在显示开发服务器之前编译整个应用程序。

页面级编译

在 Next.js 11 中,我们开始只编译您请求的页面上的代码。

这更好,但并不完美。当导航到 /users 时,我们将捆绑所有客户端和服务器模块、动态导入的模块以及引用的 CSS 和图像。这意味着如果页面的很大一部分隐藏在视图之外,或者隐藏在选项卡后面,Next.js 仍然会编译它。

请求级编译

Turbopack 足够聪明,可以只编译您请求的代码。这意味着如果浏览器请求 HTML,我们只编译 HTML——而不是 HTML 引用的任何内容。

如果浏览器需要一些 CSS,我们将只编译它——而不编译引用的图像。如果你使用 next/dynamic 加载一个大型图表库吗?在显示图表的选项卡显示之前将不编译它。 Turbopack 甚至知道不编译源映射,除非您的 Chrome DevTools 是打开的。 如果我们使用原生 ESM,我们会得到类似的行为。除了 Native ESM 向服务器产生大量请求外,正如我们为什么选择 Turbopack 部分所讨论的那样。通过请求级编译,我们既可以减少请求的数量,又可以使用本机速度来编译它们。正如您在我们的基准测试中所看到的,这提供了显着的性能改进。