ลองหัดใช้งาน esbuild เบื้องต้น

Published on
ESBuild
getting-started-with-esbuild
Discord

วันนี้ผมได้ลองใช้งาน esbuild ครั้งแรก ซึ่งก่อนหน้านี้ เคยได้ยิน และได้เห็นผ่านตา แต่ไม่เคยได้จับมันจริงๆ เพราะส่วนใหญ่ผมก็จะเป็นแค่ผู้ใช้งาน Library/ Frmework เลยไม่ได้ลงไปถึงขั้นต้อง config webpack/esbuild อะไรพวกนั้น

หลังจากได้ลองเล่นไปนิดๆหน่อย ก็ลองมาเขียนบทความบันทึกไว้ครับ ซึ่งส่วนใหญ่ Reference ผมก็อ่านจาก Official esbuild ครับ

esbuild คืออะไร?

esbuild เป็น JavaScript bundler/Compiler ที่เขียนด้วยภาษา Go คล้ายๆกับ Webpack, Rollup หรือ SWC คือทำหน้าที่ รวมไฟล์ แปลงไฟล์ minify ต่างๆ

Feature หลักๆ คือ

  • ES6 และ CommonJS
  • รองรับ Tree shaking (คือ module ไหนไม่ได้ import ก็ไม่ถูกยัดรวม bundle)
  • มี API สำหรับ JavaScript และ Go
  • รองรับ TypeScript และ JSX
  • Sourcmap และ Minify เป็นต้น

Install esbuild

เริ่มต้น เราจะทำการสร้างโปรเจ็คขึ้นมาก่อน ผมตั้งชื่อให้มันว่า try-esbuild ละกัน และก็ initial project ด้วย yarn

mkdir try-esbuild
cd try-esbuild

yarn init -y

จากนั้นทำการ install esbuild ลงไป

yarn add esbuild

ตัว esbuild เราก็จะสามารถเรียกจาก folder นี้ได้แล้ว:

./node_modules/.bin/esbuild --version

0.14.36

First bundle

ต่อมาลอง build จริงๆ ด้วยการใช้ React และ ReactDOM กัน ทำการติดตั้ง dependencies:

yarn add react react-dom

ต่อมาสร้างไฟล์ app.jsx ขึ้นมา มีโค๊ดดังนี้

app.jsx
import * as React from 'react'
import * as Server from 'react-dom/server'

let Greet = () => <h1>Hello, world!</h1>
console.log(Server.renderToString(<Greet />))

ตัวโค๊ดด้านบน ใช้ความสามารถของ react เพื่อ server render ทำให้เราสามารถรันด้วยคำสั่ง node ได้

ซึ่งถ้าเรารันตรงๆ แบบนี้ จะ error:

node app.jsx

ต้องทำการ bundle file ด้วย esbuild ก่อน แบบนี้:

./node_modules/.bin/esbuild app.jsx --bundle --outfile=out.js

จะได้ไฟล์ผลลัพธ์และไฟล์ output คือ out.js

out.js  507.3kb

⚡ Done in 10ms

เมื่อได้ไฟล์นี้เราสามารถรันด้วยคำสั่ง

node out.js

<h1>Hello, world!</h1>

React App

เผื่อยังไม่เห็นภาพ ลองเป็น React ฝั่ง Client บ้าง ผมสร้างโฟลเดอร์มาใหม่ ขื่อ src ข้างในมีไฟล์ App.jsx ดังนี้ (เอามาจาก template ของ create-react-app)

App.jsx
import React from 'react';

function App() {
  return (
    <div className="App">
      <header className="App-header">
				<h1>üëã Hello esbuild!</h1>
      </header>
    </div>
  );
}

export default App;

และไฟล์ src/index.jsx เป็นแบบนี้

src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';

import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

ต่อมา ก็ทำการ bundle ไฟล์ ไว้ที่โฟลเดอร์ dist (ใช้ --outdir แทน --outfile ก่อนหน้านี้)

./node_modules/.bin/esbuild --bundle src/index.jsx src/App.jsx --outdir=dist

สร้างไฟล์ index.html ขึ้นมา

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
      name="description"
      content="Hello esbuild"
    />
    <title>Hello esbuild</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>

		<script src="dist/index.js"></script>
  </body>
</html>

ทดลองเปิด Local server

python3 -m http.server

หรือใช้ Serve

npx serve

จะเห็นข้อความว่า 👋 Hello esbuild!

Build scripts

จะดีกว่ามั้ย ถ้าเราเปลี่ยนคำสั่งไปใช้ package scripts แทน ที่จะต้องมาพิมพ์บ่อยๆ? เพิ่มลงไปใน package.json

package.json
{
  "scripts": {
    "build": "esbuild --bundle src/index.jsx src/App.jsx --outdir=dist"
  }
}

เวลาใช้งาน ก็แค่:

yarn build

นอกจาก Command line แล้ว เราสามารถใช้ JavaScript API ได้ เหมือนกัน เช่น ผมตั้งชื่อว่า build.js

build.js
require('esbuild').build({
  logLevel: 'info',
  entryPoints: ['src/App.jsx', 'src/index.jsx'],
  bundle: true,
  outdir: 'dist',
}).catch(() => process.exit(1))

เวลาใช้งานก็ใช้ node เพื่อรัน bundle

node build.js

จะได้ผลลัพธ์เท่ากับแบบ command line

Build browser

ก่อนหน้านี้เรา build แต่ว่ายังไม่ได้ minify ให้มันเลย ไฟล์ใหญ่มาก เราสามารถ minify, sourcemap ให้มัน หรือจะกำหนด browser support ให้มันก็ได้ เช่น

esbuild app.jsx --bundle --minify --sourcemap --target=chrome58,firefox57

ทดลองเพิ่ม minify และสังเกตขนาดไฟล์ดูครับ

นอกจากนี้เราก็สามารถ bundle ไฟล์ css ได้เช่นกันครับ คำสั่งก็เหมือนกัน

esbuild --bundle app.css --outfile=out.css

Analyze

เราสามารถใช้ --analyze เพื่อดูรายละเอียดไฟล์ได้ด้วย

esbuild --bundle src/index.jsx src/App.jsx --minify --analyze --outdir=dist

จะได้ผลลัพธ์ประมาณนี้

  dist/index.js  138.8kb
  dist/App.js      7.3kb


  dist/index.js                                              138.8kb  100.0%
   ├ node_modules/react-dom/cjs/react-dom.production.min.js  126.5kb   91.1%
   ├ node_modules/react/cjs/react.production.min.js            6.4kb    4.6%
   ├ node_modules/scheduler/cjs/scheduler.production.min.js    3.9kb    2.8%
   ├ node_modules/react-dom/index.js                           253b     0.2%
   ├ src/App.jsx                                               220b     0.2%
   ├ src/index.jsx                                             188b     0.1%
   ├ node_modules/react-dom/client.js                          107b     0.1%
   ├ node_modules/react/index.js                                51b     0.0%
   └ node_modules/scheduler/index.js                            51b     0.0%

  dist/App.js                                                  7.3kb  100.0%
   ├ node_modules/react/cjs/react.production.min.js            6.3kb   86.2%
   ├ src/App.jsx                                               214b     2.9%
   └ node_modules/react/index.js                                46b     0.6%

✨  Done in 0.26s.

สรุป

วันนี้ก็ลองเล่น esbuild ไปแบบเบื้องต้น ก็ทำให้มองเห็นภาพการทำงานมากขึ้น สิ่งที่น่าสนใจและน่าลองศึกษาต่อคือการ bundle รวมไฟล์ แยกไฟล์ แยกโฟลเดอร์ อะไรพวกนั้น แล้วก็การ generate hash ให้ไฟล์ การทำหรือใช้ plugin ต่างๆ รวมถึงพวก watch / hot reload และ WASM

Happy Coding ❤️

Reference

Buy Me A Coffee
Authors
Discord