写点什么

构建系列之新一代利器 Esbuild(上)

作者:江湖修行
  • 2023-06-12
    北京
  • 本文字数:1775 字

    阅读完需:约 6 分钟

What is Esbuild?


Esbuild 是由 Figma 的 CTO 「Evan Wallace」基于 Golang 开发的一款打包工具,相比传统的打包工具,主打性能优势,在构建速度上可以快 10~100 倍。


为什么会这么快?

go 实现,编译为本地代码

大多数打包器都是用 JavaScript 编写的,esbuild 采用 Go 语言开发,相比于 单线程 + JIT 性质的解释型语言 ,使用 Go 的优势在于 :


  • 一方面可以充分利用多线程打包,并且线程之间共享内容,而 JS 如果使用多线程还需要有线程通信(postMessage)和序列化数据的开销;

  • 另一方面直接编译成机器码,而不用像 Node 一样先将 JS 代码解析为字节码,然后转换为机器码,大大节省了程序运行时间。

  • 垃圾回收,go 的堆线程之间共享,javascrit 是每个 js 线程独享一个堆。

多核并行

内部打包算法充分利用多核 CPU 优势。Esbuild 内部算法设计是经过精心设计的,尽可能充分利用所有的 CPU 内核。所有的步骤尽可能并行,这也是得益于 Go 当中多线程共享内存的优势,而在 JS 中所有的步骤只能是串行的。具体来说:解析代码生成是大部分工作,并且能够齐全并行化(链接在大多数状况下是固有的串行工作)。

极致的代码

esbuild 通过自行实现所有逻辑来避免第三方库带来的性能问题, 统一的数据结构可以减少数据转换开销, 并且可以根据需要改变架构, 当然最大的缺点就是工作量倍增.

**内存高效利用 **

ESBuild 在实现时尽量减少数据的传递以及数据的转换, ESBuild 尽量减少了对整体 AST 的传递, 并且尽可能复用 AST 数据, 其他的 Bundler 可能会在编译的不同阶段往复转换数据格式(string -> TS -> JS -> older JS -> string...,这其中会涉及复杂的编译工具链,比如 webpack -> babel -> terser,每次接触到新的工具链,都得重新解析 AST). 在内存存储效率方面 Go 也比 JavaScript 更高效.

劣势

其实也不能说是劣势,更准确地说 Esbuild 本身的限制,每个构建工具都有自己的初衷,而在更完美地实现这个目标的同时也会在一些方面做取舍:


  • 没有 TS 类型检查

  • 不能操作 AST,这个确实会带来一些不便之处,很多大型项目都会有操作 AST 的需要,所以就需要引入 babel 插件解决

  • 不支持装饰器语法

  • 产物 target 无法降级到 ES5 及以下,意味着需要 ES5 产物的场景只用 Esbuild 无法胜任

垂直场景

对于前端的构建工具来说主要有这样几个垂直的功能:


  • Bundler

  • Transformer

  • Minimizer


Esbuild 作为 transformer 性能是可以的,但 Esbuild 兼容性不足,可以不优先考虑。


Esbuild 作为 Bundler 已经被 Vite 作为开发阶段的依赖预打包工具,同时也被大量用作线上 esm CDN 服务,比如 esm.sh 等等;作为 Minimizer ,Esbuild 也已足够成熟,目前已经被 Vite 作为 JS 和 CSS 代码的压缩工具用上了生产环境。

插件机制

esbuild 插件就是一个对象,里面有 name 和 setup 两个属性,name 是插件的名称,setup 是一个函数,其中入参是一个 build 对象,这个对象上挂载了一些钩子可供我们自定义一些构建逻辑。以下是一个简单的 esbuild 插件示例:


let envPlugin = {    name: 'env',    setup(build) { // 文件解析时触发            // 将插件作用域限定于env文件,并为其标识命名空间"env-ns"             build.onResolve({            filter: /^env$/        }, args => ({            path: args.path,            namespace: 'env-ns',        })) // 加载文件时触发             // 只有命名空间为"env-ns"的文件才会被处理             // 将process.env对象反序列化为字符串并交由json-loader处理                 build.onLoad({            filter: /.*/,            namespace: 'env-ns'        }, () => ({            contents: JSON.stringify(process.env),            loader: 'json',        }))    },}require('esbuild')    .build({        entryPoints: ['app.js'],        bundle: true,        outfile: 'out.js', // 应用插件              plugins: [envPlugin],    }).catch(() => process.exit(1))
复制代码


总结


本篇文章主要围绕着 esbuild 的强大性能做介绍,快是它最大的利器,所谓天下武功唯快不破的道理大家懂得都懂,我们也是从底到上的分析了 esbuild 快的原因,下半部分主要围绕着 esbuild 的使用场景为切入点,欢迎期待。微信公众号首发,欢迎关注,转发,评论。

发布于: 刚刚阅读数: 5
用户头像

江湖修行

关注

还未添加个人签名 2021-12-05 加入

还未添加个人简介

评论

发布
暂无评论
构建系列之新一代利器Esbuild(上)_cli_江湖修行_InfoQ写作社区