Node.js のビルドツール「esbuild」について!
はじめに
esbuild は、キャッシュなしで高速なビルドを可能とする Node.js のビルドツールです。
ビルドツールには、esbuild の他に、Webpack、Gulp、Parcel、Rollup、Browserify、FuseBox などがあります。
私自身が webpack を普段使っていて、ビルドに時間がかかりすぎているのが気になり、esbuild について調べてみようと思いました。
esbuild の特徴としては、
- キャッシュなしでの高速なビルド
- ES6 と CommonJS をサポート
- ES6 の Tree shaking 対応(利用されていないコードの除去)
- JavaScript と Go による API
- TypeScript と JSX をサポート
- ソースマップの生成
- ソースコードの最小化
- プラグイン(現在、experimental で v1.0.0 より前に対応予定)
が挙げられます。
この中でも注目すべきは、そのビルド速度にあると思います。
なぜ早いのか?
-
esbuild は Go で書かれており、ネイティブコードへコンパイルしている
ほとんどのビルドツールは、Javascript によって書かれており、JIT コンパイルを使用するため、速度が遅い。
Go は、並列処理が得意で共有メモリをスレッド間で使用する。また、ヒープメモリも共有している。
よって、CPU を効率的に使用して、並列処理を行える。
-
並列で処理を行う
昨今のPCは、複数のメモリを持っているため、効率的に並列処理を行える。
-
esbuild は、0 から速度を意識して作られた
3rd パーティライブラリを使用するのに比べ、パフォーマンスに対して多くのメリットが得られる。
例えば、多くのバンドラでは、公式の TypeScript コンパイラを使ってパースしているが、そのコンパイラは、パフォーマンスを最優先として作られていない。
その点、0 からパフォーマンスを最優先として作ることで、高速にコンパイルが可能となる。
-
メモリを効率的に使用する
esbuild は、JavaScript の抽象構文ツリーへは 3 回しかアクセスしない。
また、Go は、コンパクトにしてメモリに保存するため、より効率的にメモリを使用できる。
esbuild の制限
- TypeScript の型チェックは行われない
- 別途 tsc を noEmit で実行し、チェックする必要があります
- ソースコードの変更監視が行われない
- sane などの watch ツールを利用すると良い
- CSS モジュール非対応
- 将来的に対応予定(https://github.com/evanw/esbuild/issues/20)
- CSS のバンドルに PostCSS + postcss-import を利用すると良い
- コード分割
- ES5へのトランスコンパイルができない
簡単な使い方
$ echo "{}" > package.json
$ yarn add esbuild react react-dom
import * as React from 'react'
import * as Server from 'react-dom/server'
let Greet = () => <h1>Hello, world!</h1>
console.log(Server.renderToString(<Greet />))
$ ./node_modules/.bin/esbuild app.jsx --bundle --outfile=out.js
$ node out.js
速度比較
今回は、create-react-app で作ったアプリケーションのビルド速度を比較します。
通常の React アプリケーション
$ npx create-react-app react-ts-app --template typescript
$ cd react-ts-app
$ vim tsconfig.json
target を es2016 に修正
$ vim src/App.tsx
svg 関連のコードをコメントアウト
$ sudo rm -fr build && /usr/bin/time yarn build
esbuild 版 React アプリケーション
$ npx create-react-app react-ts-app --template typescript
$ cd react-ts-app
$ vim tsconfig.json
target を es2016 に修正
$ vim src/App.tsx
svg 関連のコードをコメントアウト
const { argv } = require('process')
const { build } = require('esbuild')
const path = require('path')
const options = {
define: { 'process.env.NODE_ENV': process.env.NODE_ENV },
entryPoints: [path.resolve(__dirname, 'src/index.tsx')],
minify: argv[2] === 'production',
bundle: true,
target: 'es2016',
platform: 'browser',
outdir: path.resolve(__dirname, 'dist'),
tsconfig: path.resolve(__dirname, 'tsconfig.json')
}
build(options).catch(err => {
process.stderr.write(err.stderr)
process.exit(1)
})
$ sudo rm -fr build && /usr/bin/time node build.ts
結果
通常の React アプリケーション
3.91 real 5.40 user 0.66 sys
3.43 real 5.53 user 0.60 sys
2.96 real 5.25 user 0.57 sys
esbuild 版 React アプリケーション
0.10 real 0.12 user 0.02 sys
0.08 real 0.12 user 0.01 sys
0.08 real 0.12 user 0.01 sys
おわりに
結果を見てもらうと分かるようにかなり高速にビルド出来ました。
create-react-app で作られるアプリケーションには、svg をインポートしている部分がありますが、ここは別途プラグインでやる必要がありそうですが、
今回の目的とは違っていたので、コメントアウトで対応しました。
単純に置き換えるだけではうまくいかないところがあったり、まだ、メジャーバージョンも 1 になっていなかったりするので、様子見しつつ、タイミングを見て導入できればと思います。
開発当初は気にならないほどの速度でビルド出来ていても、開発が進むにつれてだんだんビルドが遅くなり、ストレスに感じることもあるかと思います。
そんな時にぜひ esbuild も検討してみてはいかがでしょうか?
お知らせ
Webサイト・ツール・LP作成のご依頼は、
こちらからお問い合わせいただけます。お気軽にご相談ください。
参考
Discussion