วันนี้ผมได้ลองใช้งาน 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-esbuildcd try-esbuild
yarn init -yจากนั้นทำการ install esbuild ลงไป
yarn add esbuildตัว esbuild เราก็จะสามารถเรียกจาก folder นี้ได้แล้ว:
./node_modules/.bin/esbuild --version
0.14.36First 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.cssAnalyze
เราสามารถใช้ --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
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust