背景
过去几年里,前端又推出了一堆新的构建工具🔧,例如像以轻量快速著称的 snowpack(目前已经不维护了,并推荐使用 Vite)、编译速度超越 Babel 几十倍的 SWC、打包和压缩资源速度惊人的 esbuild,以及如今呼声最高,被誉为前端下一代构建工具的 Vite。这么多的工具的推出,让以前一家独大的 webpack 突然失去的声音,甚至前端构建领域掀起了一场 去webpack 的浪潮🌊。今天就让我们简单了解下这些构建工具的代表 SWC、esbuild 和 Vite。
turbopack
增量响应式系统,又有 Rust 的速度加持
Turbopack 是建立在 Turbo 之上的,Turbo 是基于 Rust 的开源、增量记忆化框架。Turbo 可以缓存程序中任何函数的结果。当程序再次运行时,函数将不会重新运行,除非它的参数改变了。这种粒度的架构使您的程序能够在函数级别跳过大量工作
SWC
简介
受限于 JS 的语言本身效率的问题,近几年前端领域出现了不少工具被 Rust 重写,其中就包括编译 JS/TS 文件速度比 Babel 快不少的 SWC,其所对标的工具就是 Babel。
SWC 全称为 Speed Web Compiler,其是基于 Rust 实现的工具,目前被很多前端知名项目(Next.js、Parcel 和 Deno)所使用。
核心功能库
@swc/cli: CLI 命令行工具,可通过命令行编译文件。
@swc/core: 编译转码核心的 API 的集合。
swc-loader: 该模块允许您将 SWC 与 webpack 一起使用。
@swc/wasm-web: 该模块允许您使用 WebAssembly 在浏览器内同步转换代码。
@swc/jest: 该模块可以让 jest 的 tranform 速度更快。
而这些功能库几乎都能在 Babel 找到对应的库,例如 @babel/cli、@babel/core、以及 babel-loader 等。也更加印证了 SWC 的竞争对手就是 Babel。
功能介绍
编译 JS 文件
通过将一个 es6 语法的 JS 文件,编译为 es5 的语法来比较两款工具编译能力:
Babel 的编译结果:
SWC 的编译结果: 最终 SWC 只花了
0.12s,Babel 花了 1.08s,SWC 的编译速度约为 Babel 的近 9 倍🚀。
在 webpack 中使用
在 webpack 中 SWC 也可以和 babel 掰掰手腕,SWC 提供了 swc-loader,其实也跟 babel-loader 的作用差不多。
在没有无缓存的情况下比较 babel-loader 与 swc-loader 在 webpack 中的编译情况:
babel-loader 的编译耗时:
swc-loader 的编译耗时:
最终 swc-loader 只花了 4.89s 就完成了文件编译工作,而 babel-loader 花了 12.56s,可见即使是在 webpack 中,swc 的编译效率依旧很高。
swcpack
swc 的打包能力还在建设中,目前只能在 spack.config.js 文件中进行一些简单的配置,预计在 V2 版本会 SWC 的 bundle 能力会有较大的提升。
// spack.config.js
const { config } = require('@swc/core/spack')
module.exports = config({
entry: {
'web': __dirname + '/src/index.ts',
},
output: {
path: __dirname + '/lib'
},
module: {},
});配置文件
swc 有自己的配置文件 .swcrc,与 babel 的配置文件不同的是 swc 的配置文件基本做到开箱即用,不需要进行对插件或预设进行二次安装。
{
"jsc": {
"parser": {
// 语法,支持ecmascript和typescript
"syntax": "ecmascript",
// 是否解析jsx,对应插件 @babel/plugin-transform-react-jsx
"jsx": false,
// 动态加载 等同于 @babel/plugin-syntax-dynamic-import
"dynamicImport": true,
// 装饰器 等同于 @babel/plugin-syntax-decorators
"decorators": false,
//顶层await 等同于@babel/plugin-syntax-top-level-await
"topLevelAwait": false,
...
// 支持多种编译插件的配置
},
// 编译目标
"target": "es5",
// 等同babel-preset-env的松散配置
"loose": false,
// 输出代码可能依赖于辅助函数来支持目标环境。
"externalHelpers": false
},
// 压缩代码
"minify": false
}详细配置见 官网配置。
小结
优势:
- 编译速度快
- 迁移成本低,基本可以从 babel 无痛迁移并能覆盖基本的使用场景。
不足:
- 生态相比于 babel 来说不够完善,用户的覆盖面也不高,某些场景可能会有试错成本。
- 若需要深入开发的话,需要学习 Rust,有较高的学习成本。
- SWC 虽然有 bundle 能力,但是 bundle 能力还不太完善,目前其在工程化领域更像是 Compiler(编译工具)。
esbuild
简介
esbuild 基于 Golang 开发的一款打包构建工具,相比传统的打包构建工具,主打性能优势,在构建速度上可以快 10~100 倍。
下图为 esbuild 和其他的构建工具用默认配置打包 10 个 three.js 库所花费时间的对比,我们能看见 esbuild 比 webpack5 的构建速度快了很多倍。
为什么能这么快?
根据官网 esbuild 的 FAQ 给出解释,简要总结下 esbuild 相比于传统构建工具有以下优势:
- 语言优势。esbuild 是基于 go 语言,传统的 JS 开发的构建工具并不适合资源打包这种 CPU 密集场景下,go 更具性能优势。
- 多线程能力。go 具有多线程运行能力,而 JS 本质上就是一门单线程语言。由于 go 的多个线程是可以共享内存的,所以可以将解析、编译和生成的工作并行化。
- 从零开始。从一开始就考虑性能,不使用第三方依赖,从始至终是使用的是一致的数据结构从而避免昂贵的数据转换。
- 内存的有效利用。webpack 的工作机制在经过不同的工具链的时候,都会进行(
string => AST => string => ... => string)string 到 AST 的不断转换,这样实际上会占用更多的内存并降低速度。而 esbuild 从头到尾尽可能的共用一份 AST,从而降低内存的占用,提升编译速度。
主要功能
核心 API
esbuild 对外提供了两个核心 API——tranform 和 build,主要功能如下:
- 支持将 js、ts、jsx、tsx、css 等一系列文件的转译。
- 支持文件监听和 devServer。
- 支持 sourcemap。
- 支持 code-splitting。
- 支持 tree-shaking 和文件压缩。
- ...
详细的可见 官方文档。
esbuild-loader
也许我们单纯去使用 esbuild 去编译打包我们的项目还是比较麻烦,esbuild 所提供的 esbuild-loader 帮我们解决了这个难题。在 webpack 中我们可以通过 esbuild-loader 体验到惊人的 JS/TS 文件编译的速度和高效的压缩能力。
接下来对比下在 webpack 环境下 esbuild-loader 和 TerserPlugin 的压缩效率:
TerserPlugin 的压缩耗时:
esbuild-loader 的压缩耗时:
两者压缩之后的 JS 产物的大小几乎没有太大差别,但是 TerserPlugin 所花费的时间是 esbuild-loader 的 10 多倍。
小结
优势:
- 构建速度非常快
- 压缩能力也非常强,可支持 JS 和 CSS 的压缩。
不足:
- 其 tranform 的 API 不能将产物编译到 es5 及以下,产物无法兼容低版本的浏览器。
- 直接使用 esbuild 进行打包具有一定的使用成本,并且不能完全覆盖使用场景。
- 在代码分割和 CSS 处理方面功能还有待完善。