涡轮包的速度,比 Webpack 快 700 倍?
10 月 25 日,Next.js13 将发布。同时,Vercel还推出并开源了下一代包装工具:Turbopack。Turbopack 是一个针对 JavaScript 和 TypeScript 优化的增量打包工具,由 Webpack 创建者 Tobias Koppers 和 Next.js 团队在 Rust 中编译。Turbopack的推出吸引了很多开发者的关注,那么我们来看看Turbopack的优势吧!
涡轮包有多快?
Turbopack 建立在新的增量架构之上,可提供最快的开发体验。在小应用程序上,它的更新速度比 Vite 快 10 倍,比 Webpack 快 700 倍。对于较大的应用程序,它通常比 Vite 快 20 倍。
由于 Turbopack 仅打包开发所需的最少资源,因此启动时间特别快。在具有3000个模块的应用中,Turbopack需要1.8秒才能启动,而Vite需要11.4秒。
为什么涡轮包这么快?
涡轮包性能有两个很好的技巧:高度优化的机器代码和低级增量估计引擎,可以缓存到单个函数的级别。它的架构从Turborepo和Google的Bazel等工具中吸取了教训,这两种工具都专注于使用缓存来防止重复完成相同的工作。
涡轮增压发动机的工作原理
Turbopack 之所以如此之快,是因为它建立在可重用的 Rust 库之上,该库支持称为 Turbo 引擎的增量估计。这是它的工作原理。
在Turbopack驱动的程序中,单个功能可以标记为“待记住”。当调用这些函数时,Turbo 引擎会记住它们的名称以及它们返回的内容。之后,它会将其保存在视频内存缓存中。下面是一个简化的示例:
我们首先在两个文件中调用 readFile,api.ts 和 sdk.ts。然后打包这些文件,将它们拼接在一起,最后得到完整的捆绑包。所有此类函数调用的结果都保存在缓存中供以后使用。
由于 sdk.ts 的结果已更改,因此需要再次打包,然后再次拼接。重要的是,api.ts没有改变。只需从缓存中读取其结果并将其传递给 concat。因此,通过不阅读和重新包装来节省时间。
Turbo 引擎目前将其缓存存储在视频内存中。这意味着缓存将与运行它的进程一样长,这对 Devserver 来说非常有用。将来,计划是将此缓存保存到文件系统或像 Turborepo 这样的远程缓存中。这意味着 Turbopack 可以记住跨运行和机器完成的工作。
这些技巧使 Turbopack 在估计应用程序的增量更新方面非常快,优化 Turbopack 以处理开发中的更新,这意味着 Devserver 将始终响应更改。
根据需要编译
Turbo 引擎有助于在 Devserver 上提供极快的更新,但还有另一个重要的指标需要考虑 - 启动时间。开发服务器开始运行的速度越快,它开始工作的速度就越快。有两种方法可以使该过程更快:更快地工作或减少工作。为了启动 Devserver,减少工作负载的方法是仅编译启动所需的代码。
(1) 页面级编译
下一个.js版本在 2-3 年前编译了整个应用程序,然后显示 Devserver。从 Next.js11 开始,仅编译请求页上的代码。这会更好,但并不完美。导航到 /users 时,将打包所有客户端和服务器模块、动态导出的模块以及引用的 CSS 和图像。这意味着,如果页面的很大一部分隐藏在视图中webpack 不是内部,或者隐藏在选项卡前面,它仍将被编译。
(2)诉状级汇编
Turbopack足够聪明,只能编译请求代码。这意味着,如果浏览器请求 HTML,它只会编译 HTML,而不是 HTML 引用的任何内容。如果浏览器需要CSS,它只会编译CSS,而不是它引用的图像,Turbopack甚至知道除非ChromeDevTools打开,否则不会编译源映射。通过请求方级别的编译,请求方的数量减少,性能显著提高。
为什么要基于 Rust 进行开发?
Turbopack 基于 Rust,每次 Next.js 团队将基于 JavaScript 的工具迁移到基于 Rust 的工具时,他们都会看到巨大的改进。接下来.js替换了 JavaScript 编译器 Babel,它将编译速率提高了 17 倍,并替换了 Terser,将压缩率提高了 6 倍,同时还减少了加载时间和带宽使用。
为什么选择涡轮增压?
Turbopack的创建是为了提高Next.js的速度,希望它能取代Webpack成为下一代Web打包工具。那么为什么不选择新一代的打包工具esbuild和SWC,而是选择创建自己的呢?
增量估计数
通常,有两种方法可以推进流程:减少工作量或并行工作。如果你想构建最快的包装工具,你需要用力驱动这两个杠杆。决定为分布式和增量行为创建一个可重用的 Turbo 构建引擎。Turbo 引擎的工作方式类似于函数调用的调度程序,允许在所有可用内核上并行调用函数。Turbo 引擎缓存它调度的所有功能的结果,这意味着它永远不必两次执行相同的工作。简而言之,它以最大速率完成最少的工作量。
其他工具采用不同的方法来“减少工作量”。例如,Vite 通过在开发模式下使用本机 ESM 来最大程度地减少工作量。在底层,Vite 使用 esbuild 来完成许多任务。esbuild是一个非常快速的打包程序,它不会强迫我们使用本机ESM。但是出于几个原因,决定不使用esbuild:
具有增量估计的 Rust 驱动的打包器在更大范围内比 esbuild 性能更好。
懒惰包装
Next.js的早期版本尝试在开发模式下打包整个Web应用程序,这不是最佳的。Next.js 的现代版本仅打包 Devserver 请求的页面。例如,如果你移动到localhost:3000,它将只打包pages/index.jsx和它导出的模块。
这些更“懒惰”的方式是快速开发服务器的关键。Esbuild没有“懒惰”包装的概念 - 它是全有或全无的。Turbopack 的开发模型根据请求创建应用程序导出和导入的最小图表,并且仅打包所需的最少代码。
此策略使 Turbopack 在首次启动 Devserver 时速度极快。只需估计呈现页面所需的代码,然后将其发送到单个块上的浏览器。在大规模应用中,这最终比本机ESM快得多。
这就是Turbopack进来的原因。
涡轮包的特点
构建 Web 应用程序的做法尤其多样化。仅在CSS中,就有SCSS,Less,CSSModule,PostCSS等。React、Vue 和 Svelte 等框架需要自定义设置。
在构建打包工具时,我们希望它开箱即用,无需配置,并且可以通过插件获得一些功能。目前,Turbopack 仍处于 alpha 阶段,在当前状态下,Turbopack 尚不可配置,因此插件尚不可用。
以下是 Turbopack 默认配置中开箱即用的功能,以及将来将通过插件配置的功能:
TurbopackvsVitevsWebpackTurbopackvsVite
Turbopack在两个关键指标上优于Vite。
(1) 开发服务器启动时间
Turbopack 的 Devserver 启动速度比 Vite 快得多。在包含 1000 个模块的应用程序中,Vite 需要 4.8 秒才能启动。涡轮包启动仅需 0.9 秒,速度快 5.5 倍。在小型应用中webpack 不是内部,这些差异将是一致的。在 30,000 个模块的应用中,Turbopack 的启动速度比 Vite 快 5.4 倍。
(2) 代码更新
修改文件时,它需要向浏览器显示更改。它的速度越快,反馈回路就越紧密,释放速度就越快。在包含 1,000 个模块的应用程序中,Turbopack 修改文件的速度比 Vite 快 5.8 倍。
TurbopackvsWebpack
Turbopack的增量架构在两个关键指标上超过了Webpack的速度。
(1) 开发服务器启动时间
Turbopack 的开发服务器启动速度比 Webpack 快得多。Next.js12 在引擎盖下使用 Webpack,它可以在 3.4 秒内在 1000 个模块的应用程序上启动构建服务器。涡轮增压组的启动速度提高了 0.9 秒至 3.9 倍。
(2) 代码更新
在开发服务器上执行的最常见操作是修改文件。修改文件时,它需要向浏览器显示更改。它的速度越快,反馈回路就越紧密,释放速度就越快。在包含 1000 个模块的应用程序中,Turbopack 对文件更改的反应速度比 Webpack 快 8.9 倍:
涡轮增压组的未来
到目前为止,Turbopack 在 Next.jsv13 中可用。未来,将发布独立的CLI,插件API,以及对Svelte和Vue等其他框架的支持。
Turbopack 将用于 Next.js13Devserver。它将为闪电般的HMR提供支持,并将原生支持React服务器端组件,以及TypeScript,JSX,CSS等。Webpack 用户还可以期待使用 Turbopack 进入基于 Rust 的未来增量迁移路径。
在Webpack创建者Tobias Koppers的带领下,Turbopack将成为Web的下一代打包工具。
参考:
¥9.9 抢980元“麦当劳+麦当劳”高级卡!
全年无限次使用!
文章目录
巴别塔(Tower of Babel),圣经中,为了阻止人们建造巴别塔(Tower of Babel),上帝让人类讲不同的语言,使人类无法互相交流,所以计划失败了,人类从此散乱。
JavaScript的babel就是将各个版本的JavaScript翻译成同一个版本或者为它们提供统一的运行环境。
babel相关包
7.0前后版本变化较大。 许多包的名称已更改。 为了统一名称,改为@babel开头。 例如,babel-core 更改为@babel/core。
图表
7.0之前 7.0之后备注
babel 核心
@babel/核心
babel-polyfill
@babel/polyfill
7.4 后已弃用,使用其他替代方案
babel 运行时
@babel/运行时
封装简介 备注
巴别塔装载机
webpack 加载代码的插件。
webpack 使用
@babel/核心
babel的核心是编译器,提供了转换API。
箭头函数、class句等转换语法
核心js
为低版本浏览器提供套接字的库。 分为core-js@2和@core-js@3
提供如:Promise全局变量实例方法Array.fill等。
再生器运行时
提供独立的函数运行时,es5环境下的es6生成器实现
包含再生器模块。 生成器函数(function*()、yeil)、async、await 函数。
@babel/polyfill
为低版本浏览器提供了socket库,本质上由core-js和regenerator-runtime组成。
7.4 弃用
@babel/助手
定义一些处理新句子关键词的辅助函数,并将必要的辅助函数作为模块导入到代码中
@babel/core 在处理时也会将辅助函数插入到代码中。 其处理方式是在每个文件中插入相应的辅助函数,而@babel/helpers则将辅助函数作为模块导入。 一个文件只需要调用这个模块即可。这个包基本上可以看成是Babel自己用的,我们自己不需要,而且好像也不让我们用
@babel/插件转换运行时
用于插入垫圈的插件
删除转换过程中@babel/core插入的函数,并替换为@babel/helpers中的函数,
@babel/运行时
包含@babel/helpers 和 regenerator-runtime
函数是这两个包的组合
@babel/runtime-corejs2
由 core-js@2、@babel/helpers 和 regenerator-runtime 组成
这三个包的功能组合
@babel/runtime-corejs3
由 core-js@3、@babel/helpers 和 regenerator-runtime 组成
这三个包的功能组合
@babel/预设环境
插件设置预设配置,
此预设包含所有转换 ECMA 标准句型的插件,但不包括各个阶段的动作。
@babel/preset-stage-X
插件集,包括stage-0,1,2,3
V7.0之后弃用,这些插件的特性并不一定确定进入最终标准,因为有些人可能在生产环境中不受限制地使用它们并导致一些问题,所以babel官方弃用了它们。
查看每个提案处于哪个阶段
关于插件集@babel/preset-env:当我们执行语言转换为浏览器提供polyfills时,我们有几个选项,
import all,即引入所有的垫片,这样我们就可能不会使用到一些功能,而引入它们的垫片白白减少了很多无效代码。 文件中介绍了手动按需引用,比如当前文件使用了哪些。 比如我们的文件中使用Promise,我们会在文件中引入它webpack 环境配置,import '@babel/runtime-corejs3/core-js/promise',这样带来的问题是,如果多个文件使用它,每个文件必须介绍一下。 使用webpack打包,我们可以收集所有需要的垫片并导入到入口文件中,这样我们只需要导入一次即可。但是如果垫片很多,入口文件上就会出现一排排重复的类似代码
import "@babel/runtime-corejs3/core-js/promise";
import "@babel/runtime-corejs3/core-js/xxx";
import "@babel/runtime-corejs3/core-js/xxx";
import "@babel/runtime-corejs3/core-js/xxx";
为了处理此类代码,引入了一组插件。 插件parade根据配置手动转换句型并导入垫片,所以不需要在入口处放置大量声明。
在 webpack 中使用
安装相关包
npm 我 @babel/core @babel/preset-env babel-loader -D
配置网络包
// webpack.config.js
module: {
rules: [
...
{
test: /.js$/,
exclude: /node_moduels/,
use: {
loader: "babel-loader"
}
}
],
}
....
配置巴贝尔
babel.config.json(官方推荐)。 .babelrc 是 .babelrc.json 的别名,“babel”属性是在 package.json 中创建的。 babelrc.json 使用了 webpack 的 loader 配置。 也可以使用js文件动态生成,只要文件导出导入符合babel配置即可,但是由于配置是动态生成的,所以配置只是在运行时获取,所以可能会影响缓存、IDE等功能手动完成。
// babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = [ ... ];
const plugins = [ ... ];
return {
presets,
plugins
};
}
babel.config.json、.babelrc 和 .babelrc.json 实际上只是一个内容相同的 JSON 文件。 而且package.json和loader中配置babel内容没有太大区别。
官方相关配置文档
根据自己的需要选择配置文件类型。 。 。 。 。
就目前的情况来说,首先选择.babelrc进行配置。
{
"presets": [
[
"@babel/preset-env", // 也可以简写为 “@babel/env”
{
"targets": {
"chrome": "68",
"ie": "11"
}
}
]
]
}
关于目标
目标环境可以通过(推荐)来指定,很多项目都是用这个来确定目标环境的。
如果提供了.browserslistrc,则不能再指定目标,因为目标将覆盖.browserslistrc的配置。 如果指定目标并希望使用 .browserslistrc,则需要指定ignoreBrowserslistConfig 属性。
如何使用polyfill方法
babel-polyfill 已弃用,但 core-js 的工作原理类似。
全部导入import "core-js";,也可以根据自己的需要导入哪些。
安装babel相关包
npm i @babel/core @babel/preset-env babel-loader -D
配置文件
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": "68",
"ie": 10
},
"useBuiltIns": "entry",
"corejs": 2
}
]
]
}
参数介绍,
target:转换结果的目标,代码最终的执行环境,根据目标环境判断是否需要转换句子类型,并对这些句子类型进行转换,如箭头函数、类语法等。
useBuiltIns: false | “进入”| “用法”
选项说明
假(默认)
此时corejs属性无效,不引入垫片,只有对目标属性进行判断后才转换对应的句型。
“入口”
在入口处自动引入垫片,并根据目标环境手动完成所有缺失的功能,无论我们的代码是否使用了相应的功能。
“用法”
在测量文件中所需使用的垫片的同时自动检查目标环境,并手动导入相应的垫片。 (官方推荐),目标环境不支持并且我们的代码不使用将无法完成。
核心js: 2| 3
corejs 属性仅在 useBuiltIns 设置为非 false 时才有效。
corejs中设置不同参数时注意安装对应的包。 如果useBuiltIns为“入口”,也需要在入口处自动导入。
corejs 选项需要额外安装的包注释
2(默认)
npm 我 core-js@2 -S
已停止添加
npm 我 core-js@3 -S
现在如果通过 npm i core-js -S 安装时不指定版本,则默认安装 3。
使用转换运行时
runtime-transform 和 core-js 类似,但是transform-runtime提供的沙箱式的washer单独生成一个运行沙箱,不会污染全局变量。一般在代码运行在我们不具备的环境中时使用控制,例如; 发布的 npm 包
安装相关包
npm 我 @babel/core @babel/preset-env @babel/plugin-transform-runtime babel-loader -D
配置文件
// .babelrc
{
"presets": [["@babel/preset-env"]],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
corejs设置不同的参数时,需要安装相应的包。
corejs选项描述了需要安装的附加包
假(默认值)
只辅助转换句型,如:箭头函数、类语法
npm 我@babel/runtime -S
在 false 的基础上减少对全局变量和静态方法的支持,如 Promise、Array.from
npm 我@babel/runtime-corejs2 -S
在2的基础上,减少支持[].includes()等实例方法
npm 我@babel/runtime-corejs3 -S
两种使用方式的区别在于是否污染地球环境。 不能同时使用。
在VUE中使用
VUE中推荐使用polyfill的形式。 因为使用transform-runtime时,它默认只会对我们的代码进行改造,而不会在node_module中的库中添加垫片。 如果你想让它转换 mode_module 中的代码,你需要使用 babel.config.js 来特殊配置,并且转换的结果可能无法使用。 node_module中的库可能有自己的垫片,但是如果要使用vuex的功能,浏览器控制台会报错,提示我们提供全局垫片,因为vuex没有自己的垫片。
我们自己开发网络程序。 目前最麻烦的就是如下配置,并且导入所有的polyfill,这样我们就不用担心漏掉什么了。 如果你之前有时间webpack 环境配置,如果你想优化包装,比如减少包装结果的体积,你可以再次优化。 开发初期,直接全部导入,不要用哪个再引入哪个,不然来回改起来就有点麻烦了。
安装 npm i @babel/core @babel/preset-env babel-loader -D 和 npm i core-js@3 -S 的软件包;
在文件入口index.js开头引入core-js
// index.js
import 'core-js/stable`
或者可以在webpack打包配置中减少core-js;
// webpack.config.js
...
module.exports = {
...
entry: {
main: ["core-js/stable", "./src/index.js"]
},
...
}
巴贝尔配置文件
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
// target 要根据自己的情况进行配置
"targets": {
"chrome": "68",
"ie": 10
},
"useBuiltIns": "entry",
"corejs": 3
}
]
]
}
发表评论