天天看点

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

前言

本篇文章接上文,通过尝试使用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/[email protected]'
           

业界落地方案

1.代码压缩工具

Esbuild 有非常优秀的代码压缩能力,有着比传统的压缩工具一个量级以上的性能差距。Vite 在 2.6 版本也官宣在生产环境中直接使用 Esbuild 来压缩 JS 和 CSS 代码。 

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

2.Bundler库

Vite 中在开发阶段使用 Esbuild 来进行依赖的预打包,将所有用到的第三方依赖转成 ESM 格式 Bundle 产物,并且未来有用到生产环境的打算。

同时业界也有一些平台基于纯 Esbuild 来做线上 cjs -> esm 的 CDN 服务,比如 esm.sh :

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

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二次封装的构建工具。

欢迎各位coder关注微信公众号,文章首发。