天天看點

建構系列之新一代利器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關注微信公衆号,文章首發。