ลองหัดใช้งาน esbuild เบื้องต้น
วันนี้ผมได้ลองใช้งาน 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
ขึ้นมา มีโค๊ดดังนี้
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)
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
เป็นแบบนี้
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
ขึ้นมา
<!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
{
"scripts": {
"build": "esbuild --bundle src/index.jsx src/App.jsx --outdir=dist"
}
}
เวลาใช้งาน ก็แค่:
yarn build
นอกจาก Command line แล้ว เราสามารถใช้ JavaScript API ได้ เหมือนกัน เช่น ผมตั้งชื่อว่า 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
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit