前言
本篇文章接上文,通过尝试使用esbuild的能力和业界的落地方案作为切入点继续深入esbuild的原理。
尝试Esbuild
ESBuild在API层面上非常简洁, 主要的API只有两个: Transform和Build, 这两个API可以通过CLI, JavaScript, Go的方式调用
Transform主要用于对源代码的转换, 接受的输入是字符串, 输出的是转换后的代码
# 用CLI方式调用, 将ts代码转化为js代码
echo 'let x: number = 1' | esbuild --loader=ts => let x = 1;
Build主要用于构建, 接受的输入是单文件或文件集合
// 用JS模式调用build方法
require('esbuild').buildSync({
entryPoints: ['in.js'],
bundle: true,
outfile: 'out.js',
})
ESBuild的内容类型(Content Type)包括了ES在打包时可以解析的文件类型, 这一点和Webpack的loader概念类似, 下面的例子是在打包时用JSX Loader解析JS文件。
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: {
'.js': 'jsx'
},
outfile: 'out.js',
})
借助esbuild的能力:如果你觉得目前完全使用ESBuild还不成熟, 也可以在Webpack体系中使用ESBuild的loader来替代babel用于进行代码转换, 除此之外, esbuild-loader[5]还可以用于JS & CSS的代码最小化.
const { ESBuildMinifyPlugin } = require('esbuild-loader')
module.exports = {
rules: [{
test: /.js$/,
// 使用esbuild作为js/ts/jsx/tsx loader
loader: 'esbuild-loader',
options: {
loader: 'jsx',
target: 'es2015'
}
}, ],
// 或者使用esbuild-loader作为JS压缩工具
optimization: {
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015'
})
]
}
}
注意:前面说过Esbuild转换的代码是无法降级到 ES5 及以下
使用 Esbuild 的虚拟模块,可以完成很丰富的功能,比如模块名当做一个函数来进行编译,甚至可以在编译阶段实现函数递归的过程。比如这个 Esbuild 插件:
{
name: 'fibo',
setup(build) {
build.onResolve({
filter: /^fib\(\d+\)/
}, args = > {
return {
path: args.path,
namespace: 'fib'
}
}) build.onLoad({
filter: /^fib\(\d+\)/,
namespace: 'fib'
}, args = > {
const match = /^fib\((\d+)\)/.exec(args.path);
n = Number(match[1]);
console.log(n);
let contents = n < 2 ? `export
default $ {
n + 1
}` : `import n1 from 'fib(${n - 1})'
import n2 from 'fib(${n - 2})'
export
default n1 + n2`
return {
contents
}
})
}
}
引入这个插件,可以解析如下的 import 语句:
import fib5 from 'fib(5)' console.log(fib5) // 13
所有的模块都是虚拟模块,在真实文件系统中并不存在,另外,还能借助虚拟模块来进行 URL Import,支持如下的 import 代码:
import React from 'https://esm.sh/react@17'
业界落地方案
1.代码压缩工具
Esbuild 有非常优秀的代码压缩能力,有着比传统的压缩工具一个量级以上的性能差距。Vite 在 2.6 版本也官宣在生产环境中直接使用 Esbuild 来压缩 JS 和 CSS 代码。
2.Bundler库
Vite 中在开发阶段使用 Esbuild 来进行依赖的预打包,将所有用到的第三方依赖转成 ESM 格式 Bundle 产物,并且未来有用到生产环境的打算。
同时业界也有一些平台基于纯 Esbuild 来做线上 cjs -> esm 的 CDN 服务,比如 esm.sh :
3.小程序编译
对于小程序的场景,也可以使用 Esbuild 来代替 Webpack,大大提升编译速度,对于 AST 的转换则通过 Esbuild 插件嵌入 SWC 来实现,实现快速编译。
4.Web构建
Web 场景就显得比较复杂了,对于兼容性和周边工具生态的要求比较高,比如低浏览器语法降级、CSS 预编译器、HMR 等等,如果要用纯 Esbuild 来做,还需要补充很多能力。
已有大佬基于 Esbuild 实现了一套 Web 开发脚手架 ewas,已经在 Github 开源,仓库地址: https://github.com/sanyuan0704/ewas。
如今 Remix 1.0 正式发布,底层使用 Esbuild 构建,带来了极致的性能体验,成为 Next.js 强有力的竞争对手。
但总体来说,目前 Esbuild 对于真实的 Web 场景还有很多能力不支持,还有一些硬伤,包括语法不支持降级到ES5,拆包不灵活、不支持 HMR,对于真正能作为 Webpack 一样的构建工具来讲还有很长的路要走。
总结
Esbuild的性能是其一大利器,这对于很多开发者和框架都会是一个优先考虑的因素,我们夜看到在整个生态系统中其也在慢慢的进行渗透,但就目前而言还不能完全替代Webpack等主流工具,毕竟整体生态环境有待完善。
建议如果想尝试esbuild的能力,但已有的基础设施稳定并且替换成本较大时, 可以尝试渐进式的利用新工具(loader)或者Vite这种基于ESBuild二次封装的构建工具。
微信首发,欢迎关注微信公众号:江湖修行,感谢各位老铁。