Logo Zephyrnet

Giới thiệu về esbuild Bundler — SitePoint

Ngày:

xây dựng là một gói nhanh có thể tối ưu hóa mã JavaScript, TypeScript, JSX và CSS. Bài viết này sẽ giúp bạn bắt kịp tốc độ với esbuild và chỉ cho bạn cách tạo hệ thống xây dựng của riêng mình mà không cần các phụ thuộc khác.

Mục lục
  1. ESbuild hoạt động như thế nào?
  2. Tại sao gói?
  3. Tại sao nên sử dụng esbuild?
  4. Tại sao Tránh esbuild?
  5. Bắt đầu siêu nhanh
  6. Dự án mẫu
  7. Tổng Quan Dự Án
  8. Cấu hình esbuild
  9. Gói JavaScript
  10. gói CSS
  11. Quan sát, xây dựng lại và phục vụ
  12. Tổng kết

ESbuild hoạt động như thế nào?

Các khuôn khổ như Sống đã áp dụng esbuild, nhưng bạn có thể sử dụng esbuild như một công cụ độc lập trong các dự án của riêng mình.

  • esbuild gói mã JavaScript vào một tệp duy nhất theo cách tương tự như các gói như Tập hợp. Đây là chức năng chính của esbuild và nó giải quyết các mô-đun, báo cáo các sự cố cú pháp, "lắc cây" để xóa các chức năng không sử dụng, xóa các câu lệnh ghi nhật ký và trình gỡ lỗi, thu nhỏ mã và cung cấp bản đồ nguồn.

  • esbuild bó mã CSS thành một tập tin duy nhất. Nó không phải là sự thay thế hoàn toàn cho các bộ xử lý trước như Sass or đăngCSS, nhưng esbuild có thể xử lý các phần, vấn đề cú pháp, lồng, mã hóa nội dung nội tuyến, bản đồ nguồn, tự động thêm tiền tố và thu nhỏ. Đó có thể là tất cả những gì bạn cần.

  • esbuild cũng cung cấp một máy chủ phát triển cục bộ với gói tự động và tải lại nóng, do đó không cần phải làm mới. Nó không có tất cả các tính năng được cung cấp bởi Đồng bộ hóa trình duyệt, nhưng nó đủ tốt cho hầu hết các trường hợp.

Đoạn mã dưới đây sẽ giúp bạn hiểu các khái niệm esbuild để bạn có thể điều tra thêm các cơ hội cấu hình cho các dự án của mình.

Tại sao gói?

Gói mã vào một tệp duy nhất mang lại nhiều lợi ích khác nhau. Dưới đây là một số trong số họ:

  • bạn có thể phát triển các tệp nguồn nhỏ hơn, độc lập, dễ bảo trì hơn
  • bạn có thể lint, làm đẹp và kiểm tra cú pháp mã trong quá trình đóng gói
  • gói có thể loại bỏ các chức năng không sử dụng - được gọi là rung cây
  • bạn có thể gói các phiên bản thay thế của cùng một mã và tạo mục tiêu cho các trình duyệt cũ hơn, Node.js, Deno, v.v.
  • các tệp đơn lẻ tải nhanh hơn nhiều tệp và trình duyệt không yêu cầu hỗ trợ mô-đun ES
  • gói cấp độ sản xuất có thể cải thiện hiệu suất bằng cách giảm thiểu mã và xóa các câu lệnh ghi nhật ký và gỡ lỗi

Tại sao nên sử dụng esbuild?

Không giống như các gói JavaScript, esbuild là một tệp thực thi Go được biên dịch, thực hiện xử lý song song nặng. Nó nhanh và nhanh hơn hàng trăm lần so với Rollup, Parcel hoặc Webpack. Nó có thể tiết kiệm hàng tuần thời gian phát triển trong suốt vòng đời của một dự án.

Ngoài ra, esbuild còn cung cấp:

  • gói và biên dịch tích hợp cho JavaScript, TypeScript, JSX và CSS
  • API cấu hình dòng lệnh, JavaScript và Go
  • hỗ trợ cho các mô-đun ES và CommonJS
  • một máy chủ phát triển cục bộ với chế độ xem và tải lại trực tiếp
  • bổ sung để thêm chức năng
  • tài liệu toàn diện và một công cụ thí nghiệm trực tuyến

Tại sao Tránh esbuild?

Tại thời điểm viết bài này, esbuild đã đạt đến phiên bản 0.18. Nó đáng tin cậy nhưng vẫn là một sản phẩm beta.

esbuild được cập nhật thường xuyên và các tùy chọn có thể thay đổi giữa các phiên bản. Các tài liệu hướng dẫn khuyên bạn nên gắn bó với một phiên bản cụ thể. Bạn có thể cập nhật nó, nhưng bạn có thể cần di chuyển các tệp cấu hình của mình và tìm hiểu kỹ tài liệu mới để khám phá những thay đổi đột phá.

Cũng lưu ý rằng esbuild không thực hiện kiểm tra kiểu TypeScript, vì vậy bạn vẫn cần chạy tsc -noEmit.

Bắt đầu siêu nhanh

Nếu cần, hãy tạo một dự án Node.js mới với npm init, sau đó cài đặt esbuild cục bộ dưới dạng phụ thuộc phát triển:

npm install esbuild --save-dev --save-exact

Việc cài đặt yêu cầu khoảng 9 MB. Kiểm tra nó hoạt động bằng cách chạy lệnh này để xem phiên bản đã cài đặt:

./node_modules/.bin/esbuild --version

Hoặc chạy lệnh này để xem trợ giúp CLI:

./node_modules/.bin/esbuild --help

Sử dụng API CLI để đóng gói tập lệnh nhập cảnh (myapp.js) và tất cả các mô-đun đã nhập của nó vào một tệp duy nhất có tên bundle.js. esbuild sẽ xuất một tệp bằng định dạng biểu thức hàm (IIFE) mặc định, được nhắm mục tiêu theo trình duyệt, được gọi ngay lập tức:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

Bạn có thể cài đặt esbuild theo những cách khác nếu bạn không sử dụng Node.js.

Dự án mẫu

Tải xuống các tệp ví dụ và một cấu hình esbuild từ Github. Đó là một dự án Node.js, vì vậy hãy cài đặt phần phụ thuộc esbuild duy nhất với:

npm install

Xây dựng các tập tin nguồn trong src đến một build thư mục và bắt đầu một máy chủ phát triển với:

npm start

Bây giờ điều hướng đến localhost:8000 trong trình duyệt của bạn để xem trang web hiển thị đồng hồ thời gian thực. Khi bạn cập nhật bất kỳ tệp CSS nào trong src/css/ or src/css/partials, esbuild sẽ nhóm lại mã và tải lại trực tiếp các kiểu.

dự án đồng hồ ví dụ esbuild

Ấn Bản Ctrl|Cmd + Ctrl|Cmd để dừng máy chủ.

Tạo bản dựng sản xuất để triển khai bằng cách sử dụng:

npm run build

Kiểm tra các tệp CSS và JavaScript trong build thư mục để xem các phiên bản rút gọn mà không có bản đồ nguồn.

Tổng Quan Dự Án

Trang đồng hồ thời gian thực được xây dựng trong một build thư mục sử dụng các tập tin nguồn từ src.

Sản phẩm package.json tập tin xác định năm npm kịch bản. Việc đầu tiên xóa các build danh mục:

"clean": "rm -rf ./build",

Trước khi bất kỳ gói nào xảy ra, một init script chạy clean, tạo một cái mới build thư mục và bản sao:

  1. một tệp HTML tĩnh từ src/html/index.html đến build/index.html
  2. hình ảnh tĩnh từ src/images/ đến build/images/
"init": "npm run clean && mkdir ./build && cp ./src/html/* ./build/ && cp -r ./src/images ./build",

An esbuild.config.js tệp kiểm soát quy trình đóng gói esbuild bằng API JavaScript. Điều này dễ quản lý hơn là chuyển các tùy chọn tới API CLI, điều này có thể trở nên khó sử dụng. MỘT npm bundle script chạy init tiếp theo node ./esbuild.config.js:

"bundle": "npm run init && node ./esbuild.config.js",

Hai cái cuối cùng npm tập lệnh chạy bundle với một trong hai production or development tham số được truyền cho ./esbuild.config.js để kiểm soát việc xây dựng:

"build": "npm run bundle -- production",
"start": "npm run bundle -- development"

Thời Gian ./esbuild.config.js chạy, nó sẽ xác định xem nó có nên tạo các bản rút gọn hay không production tệp (mặc định) hoặc development các tệp có cập nhật tự động, bản đồ nguồn và máy chủ tải lại trực tiếp. Trong cả hai trường hợp, gói esbuild:

  • tệp CSS mục nhập src/css/main.css đến build/css/main.css
  • tệp JavaScript mục nhập scr/js/main.js đến build/js/main.js

Cấu hình esbuild

package.json có một "type" of "module" vì vậy tất cả .js các tệp có thể sử dụng Mô-đun ES. Các esbuild.config.js nhập tập lệnh esbuild và bộ productionMode đến true khi đóng gói để sản xuất hoặc false khi đóng gói để phát triển:

import { argv } from 'node:process';
import * as esbuild from 'esbuild'; const productionMode = ('development' !== (argv[2] || process.env.NODE_ENV)), target = 'chrome100,firefox100,safari15'.split(','); console.log(`${ productionMode ? 'production' : 'development' } build`);

mục tiêu gói

Lưu ý rằng biến mục tiêu định nghĩa một mảng các trình duyệt và số phiên bản sẽ sử dụng trong cấu hình. Điều này ảnh hưởng đến đầu ra đi kèm và thay đổi cú pháp để hỗ trợ các nền tảng cụ thể. Ví dụ: esbuild có thể:

  • mở rộng làm tổ CSS gốc vào bộ chọn đầy đủ (lồng nhau sẽ vẫn còn nếu "Chrome115" là mục tiêu duy nhất)
  • thêm các thuộc tính tiền tố nhà cung cấp CSS khi cần thiết
  • điền vào ?? nhà điều hành liên kết nullish
  • tẩy # từ các trường lớp riêng

Cũng như các trình duyệt, bạn cũng có thể nhắm mục tiêu nodees các phiên bản như es2020esnext (các tính năng JS và CSS mới nhất).

Gói JavaScript

API đơn giản nhất để tạo một gói:

await esbuild.build({ entryPoints: ['myapp.js'], bundle: true outfile: 'bundle.js'
});

Điều này sao chép lệnh CLI được sử dụng ở trên:

./node_modules/.bin/esbuild myapp.js --bundle --outfile=bundle.js

Dự án ví dụ sử dụng các tùy chọn nâng cao hơn như xem tệp. Điều này đòi hỏi một quá trình xây dựng lâu dài bối cảnh trong đó thiết lập cấu hình:


const buildJS = await esbuild.context({ entryPoints: [ './src/js/main.js' ], format: 'esm', bundle: true, target, drop: productionMode ? ['debugger', 'console'] : [], logLevel: productionMode ? 'error' : 'info', minify: productionMode, sourcemap: !productionMode && 'linked', outdir: './build/js' });

cung cấp esbuild hàng chục tùy chọn cấu hình. Dưới đây là danh sách những cái được sử dụng ở đây:

  • entryPoints xác định một mảng các điểm nhập tệp để đóng gói. Dự án ví dụ có một tập lệnh tại ./src/js/main.js.

  • format đặt định dạng đầu ra. ví dụ sử dụng esm, nhưng bạn có thể tùy ý đặt iife cho các trình duyệt cũ hơn hoặc commonjs cho Node.js.

  • bundle đặt thành true nội tuyến các mô-đun đã nhập vào tệp đầu ra.

  • target là mảng các trình duyệt mục tiêu được xác định ở trên.

  • drop là một mảng của console và / hoặc debugger tuyên bố để loại bỏ. Trong trường hợp này, các bản dựng sản xuất loại bỏ cả hai và các bản dựng phát triển giữ lại chúng.

  • logLevel xác định mức độ chi tiết của nhật ký. Ví dụ trên cho thấy các lỗi trong quá trình xây dựng sản xuất và nhiều thông báo dài dòng hơn trong quá trình xây dựng phát triển.

  • minify giảm kích thước mã bằng cách xóa các nhận xét và khoảng trắng cũng như đổi tên các biến và hàm nếu có thể. Dự án ví dụ thu nhỏ trong quá trình xây dựng sản xuất nhưng làm đẹp mã trong quá trình xây dựng phát triển.

  • sourcemap đặt thành linked (chỉ trong chế độ phát triển) tạo bản đồ nguồn được liên kết trong một .map tệp để tệp nguồn gốc và dòng có sẵn trong các công cụ dành cho nhà phát triển trình duyệt. Bạn cũng có thể thiết lập inline để bao gồm bản đồ nguồn bên trong tệp được đóng gói, both để tạo cả hai, hoặc external để tạo ra một .map tệp không có liên kết từ JavaScript đi kèm.

  • outdir xác định thư mục đầu ra của tệp đi kèm.

Gọi đối tượng bối cảnh của rebuild() phương pháp để chạy bản dựng một lần — thường dành cho bản dựng sản xuất:

await buildJS.rebuild();
buildJS.dispose(); 

Gọi đối tượng bối cảnh của watch() phương pháp để tiếp tục chạy và tự động xây dựng lại khi tệp đã xem thay đổi:

await buildJS.watch();

Đối tượng bối cảnh đảm bảo các bản dựng tiếp theo được xử lý tăng dần và chúng sử dụng lại công việc từ các bản dựng trước đó để cải thiện hiệu suất.

Tệp đầu vào và đầu ra JavaScript

Mục nhập src/js/main.js nhập tệp dom.jstime.js mô-đun từ lib thư mục con. Nó tìm thấy tất cả các phần tử với một lớp clock và đặt nội dung văn bản của chúng thành thời điểm hiện tại mỗi giây:

import * as dom from './lib/dom.js';
import { formatHMS } from './lib/time.js'; const clock = dom.getAll('.clock'); if (clock.length) { console.log('initializing clock'); setInterval(() => { clock.forEach(c => c.textContent = formatHMS()); }, 1000); }

dom.js xuất khẩu hai chức năng. main.js nhập cả hai nhưng chỉ sử dụng getAll():

 export function get(selector, doc = document) { return doc.querySelector(selector);
} export function getAll(selector, doc = document) { return Array.from(doc.querySelectorAll(selector));
}

time.js xuất khẩu hai chức năng. main.js nhập khẩu formatHMS(), nhưng điều đó sử dụng các chức năng khác trong mô-đun:

 function timePad(n) { return String(n).padStart(2, '0');
} export function formatHM(d = new Date()) { return timePad(d.getHours()) + ':' + timePad(d.getMinutes());
} export function formatHMS(d = new Date()) { return formatHM(d) + ':' + timePad(d.getSeconds());
}

Gói phát triển kết quả loại bỏ (lắc cây) get() từ dom.js nhưng bao gồm tất cả các time.js chức năng. Một bản đồ nguồn cũng được tạo ra:


function getAll(selector, doc = document) { return Array.from(doc.querySelectorAll(selector));
} function timePad(n) { return String(n).padStart(2, "0");
} function formatHM(d = new Date()) { return timePad(d.getHours()) + ":" + timePad(d.getMinutes());
} function formatHMS(d = new Date()) { return formatHM(d) + ":" + timePad(d.getSeconds());
} var clock = getAll(".clock");
if (clock.length) { console.log("initializing clock"); setInterval(() => { clock.forEach((c) => c.textContent = formatHMS()); }, 1e3);
} 

(Lưu ý rằng esbuild có thể viết lại letconst đến var cho chính xác và tốc độ.)

Gói sản xuất kết quả thu nhỏ mã thành 322 ký tự:

function o(t,c=document){return Array.from(c.querySelectorAll(t))}function e(t){return String(t).padStart(2,"0")}function l(t=new Date){return e(t.getHours())+":"+e(t.getMinutes())}function r(t=new Date){return l(t)+":"+e(t.getSeconds())}var n=o(".clock");n.length&&setInterval(()=>{n.forEach(t=>t.textContent=r())},1e3);

gói CSS

Gói CSS trong dự án ví dụ sử dụng một đối tượng ngữ cảnh tương tự như JavaScript ở trên:


const buildCSS = await esbuild.context({ entryPoints: [ './src/css/main.css' ], bundle: true, target, external: ['/images/*'], loader: { '.png': 'file', '.jpg': 'file', '.svg': 'dataurl' }, logLevel: productionMode ? 'error' : 'info', minify: productionMode, sourcemap: !productionMode && 'linked', outdir: './build/css' });

Nó định nghĩa một external tùy chọn dưới dạng một mảng các tệp và đường dẫn đến loại trừ từ bản dựng. Trong dự án ví dụ, các tập tin trong src/images/ thư mục được sao chép vào build thư mục để HTML, CSS hoặc JavaScript có thể tham chiếu trực tiếp đến chúng. Nếu điều này không được đặt, esbuild sẽ sao chép tệp vào đầu ra build/css/ thư mục khi sử dụng chúng trong background-image hoặc các thuộc tính tương tự.

Sản phẩm loader tùy chọn thay đổi cách esbuild xử lý tệp đã nhập không được tham chiếu dưới dạng tệp external tài sản. Trong ví dụ này:

  • Hình ảnh SVG trở thành nội tuyến dưới dạng URI dữ liệu
  • Hình ảnh PNG và JPG được sao chép vào build/css/ thư mục và được tham chiếu dưới dạng tệp

Tệp đầu vào và đầu ra CSS

Mục nhập src/css/main.css nhập tệp variables.csselements.css từ partials thư mục con:


@import './partials/variables.css';
@import './partials/elements.css';

variables.css xác định các thuộc tính tùy chỉnh mặc định:


:root { --font-body: sans-serif; --color-fore: #fff; --color-back: #112;
}

elements.css xác định tất cả các phong cách. Ghi chú:

  • các body có hình nền được tải từ bên ngoài images thư mục
  • các h1 được lồng bên trong header
  • các h1 có một SVG nền sẽ được nội tuyến
  • các trình duyệt mục tiêu không yêu cầu tiền tố nhà cung cấp

*, *::before, ::after { box-sizing: border-box; font-weight: normal; padding: 0; margin: 0;
} body { font-family: var(--font-body); color: var(--color-fore); background: var(--color-back) url(/images/web.png) repeat; margin: 1em;
} header { & h1 { font-size: 2em; padding-left: 1.5em; margin: 0.5em 0; background: url(../../icons/clock.svg) no-repeat; } } .clock { display: block; font-size: 5em; text-align: center; font-variant-numeric: tabular-nums;
}

Gói phát triển kết quả mở rộng cú pháp lồng nhau, nội tuyến SVG và tạo bản đồ nguồn:


:root { --font-body: sans-serif; --color-fore: #fff; --color-back: #112;
} *,
*::before,
::after { box-sizing: border-box; font-weight: normal; padding: 0; margin: 0;
}
body { font-family: var(--font-body); color: var(--color-fore); background: var(--color-back) url(/images/web.png) repeat; margin: 1em;
}
header h1 { font-size: 2em; padding-left: 1.5em; margin: 0.5em 0; background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>*{fill:none;stroke:%23fff;stroke-width:1.5;stroke-miterlimit:10}</style></defs><circle cx="12" cy="12" r="10.5"></circle><circle cx="12" cy="12" r="0.95"></circle><polyline points="12 4.36 12 12 16.77 16.77"></polyline></svg>') no-repeat;
}
.clock { display: block; font-size: 5em; text-align: center; font-variant-numeric: tabular-nums;
} 

Gói sản xuất kết quả thu nhỏ mã thành 764 ký tự (SVG được bỏ qua ở đây):

:root{--font-body: sans-serif;--color-fore: #fff;--color-back: #112}*,*:before,:after{box-sizing:border-box;font-weight:400;padding:0;margin:0}body{font-family:var(--font-body);color:var(--color-fore);background:var(--color-back) url(/images/web.png) repeat;margin:1em}header h1{font-size:2em;padding-left:1.5em;margin:.5em 0;background:url('data:image/svg+xml,<svg...></svg>') no-repeat}.clock{display:block;font-size:5em;text-align:center;font-variant-numeric:tabular-nums}

Quan sát, xây dựng lại và phục vụ

Phần còn lại của esbuild.config.js gói tập lệnh một lần cho các bản dựng sản xuất trước khi chấm dứt:

if (productionMode) { await buildCSS.rebuild(); buildCSS.dispose(); await buildJS.rebuild(); buildJS.dispose(); }

Trong quá trình xây dựng phát triển, tập lệnh tiếp tục chạy, theo dõi các thay đổi của tệp và tự động nhóm lại. Các buildCSS bối cảnh khởi chạy một máy chủ web phát triển với build/ như thư mục gốc:

else { await buildCSS.watch(); await buildJS.watch(); await buildCSS.serve({ servedir: './build' }); }

Bắt đầu xây dựng phát triển với:

npm start

Sau đó điều hướng tới localhost:8000 để xem trang.

Không giống như Browsersync, bạn sẽ cần thêm mã của riêng mình vào các trang phát triển để tải lại trực tiếp. Khi thay đổi xảy ra, esbuild sẽ gửi thông tin về bản cập nhật qua sự kiện do máy chủ gửi. Tùy chọn đơn giản nhất là tải lại toàn bộ trang khi có bất kỳ thay đổi nào xảy ra:

new EventSource('/esbuild').addEventListener('change', () => location.reload());

Dự án ví dụ sử dụng đối tượng ngữ cảnh CSS để tạo máy chủ. Đó là vì tôi muốn làm mới các thay đổi JavaScript theo cách thủ công — và vì tôi không tìm được cách để esbuild gửi sự kiện cho cả bản cập nhật CSS và JS! Trang HTML bao gồm tập lệnh sau để thay thế các tệp CSS đã cập nhật mà không cần làm mới toàn bộ trang (tải lại nóng):

<script type="module">
// esbuild server-sent event - live reload CSS
new EventSource('/esbuild').addEventListener('change', e => { const { added, removed, updated } = JSON.parse(e.data); // reload when CSS files are added or removed if (added.length || removed.length) { location.reload(); return; } // replace updated CSS files Array.from(document.getElementsByTagName('link')).forEach(link => { const url = new URL(link.href), path = url.pathname; if (updated.includes(path) && url.host === location.host) { const css = link.cloneNode(); css.onload = () => link.remove(); css.href = `${ path }?${ +new Date() }`; link.after(css); } }) });

Lưu ý rằng esbuild hiện không hỗ trợ tải lại nóng JavaScript — không phải là tôi sẽ tin tưởng nó anyway!

Tổng kết

Với một chút cấu hình, esbuild có thể đủ để xử lý tất cả các yêu cầu xây dựng sản xuất và phát triển dự án của bạn.

Có một bộ toàn diện của bổ sung nếu bạn yêu cầu chức năng nâng cao hơn. Xin lưu ý rằng những công cụ này thường bao gồm Sass, PostCSS hoặc các công cụ xây dựng tương tự, vì vậy chúng sử dụng hiệu quả esbuild làm trình chạy tác vụ. Bạn luôn luôn có thể mà tạo plugin của riêng bạn nếu bạn cần các tùy chọn tùy chỉnh, nhẹ hơn.

Tôi đã sử dụng esbuild được một năm. Tốc độ đáng kinh ngạc so với các gói tương tự và các tính năng mới xuất hiện thường xuyên. Nhược điểm nhỏ duy nhất là phá vỡ những thay đổi phát sinh bảo trì.

esbuild không tuyên bố là một công cụ xây dựng hợp nhất, tất cả trong một, nhưng có lẽ nó gần với mục tiêu đó hơn là Roma.

tại chỗ_img

Tin tức mới nhất

tại chỗ_img