+37

Bun.sh - Dấu chấm hết dành cho Node.js???

👋👋👋 Hello hello, xin chào tất cả anh em. Anh em nào đã lỡ vào đây rồi thì comment chào nhau một cái nhé cho đông vui nhé!

Trong bài viết này, mình sẽ đề cập tới một cái tên cực kỳ mới đó chính là Bun - một JavaScript Runtime mới nổi lên trong 2 năm trở lại đây (2022 - 2023) và còn hot hơn cả trong những ngày vừa qua. Bun được biết đến bởi hiệu suất hoạt động vượt trội mà nó mang lại khi so sánh với các đối thủ thời trước như Deno và Node.js.

image.png

Ngày 08/09, Bun đã chính thức release phiên bản stable 1.0.0. Đánh dấu cột mốc đầu tiên cho sự phát triển của Bun. Khác với Deno, mình nhận thấy Bun cực kỳ hứa hẹn cho một tương lai phát triển web bằng JavaScript & TypeScript khi Bun làm được rất nhiều điều đáng kinh ngạc:

  • Tương thích với Node.js 🤗
  • Tốc độ xử lý rất nhanh ⚡️
  • Đa di năng
  • Hỗ trợ TypeScript, JSX/TSX
  • Hỗ trợ syntax của cả ESM và Common JS. Không phải config nhiều thứ dưới đây nữa:
    • babel, .babelrc, @babel/preset-*
    • ts-node, ts-node-esm
    • tsx

Vậy liệu Bun có gì hay ho thật không và liệu chúng ta có nên chuyển sang dùng Bun? Hãy cùng mình khám phá ngay nhé! Hãy bắt đầu từ khái niệm JavaScript Runtime trước khi tìm hiểu sâu hơn về Bun. 🤗

JavaScript Runtime

Nhìn lại bối cảnh ra đời của JavaScript. JavaScript là ngôn ngữ lập trình được tạo ra ban đầu để tương tác với trình duyệt web và thay đổi nội dung trang web mà không cần tải lại trang. Do vậy, JavaScript đã được thiết kế để không cần phải biên dịch code. Cách thức tiếp cận rất đơn giản, trình duyệt chỉ cần tải các file code JavaScript về và thực thi nó. Lợi ích khỏi phải bàn, khi các lập trình viên dễ dàng và nhanh chóng sử dụng JavaScript vào website. Nhưng điều này kéo theo là JavaScript không thể tự chạy độc lập được!!!

Trong khoảng chục năm trở lại đây, khi sức phát triển JavaScript quá mạnh và người ta mong muốn nó vượt ra khỏi phạm vi browser. Nó bắt buộc cần một cái tương tự như browser để chạy - và đó chính là JavaScript runtime. JavaScript runtime là môi trường thực thi mã JavaScript. Nó cung cấp những thứ cần thiết như: các WebAPIs (DOM API, Canvas API, Fetch API...) để tương tác với trình duyệt, JavaScript engine để biên dịch mã JavaScipt thành mã máy (hoặc thông dịch trước khi cơ chế JIT ra đời năm 2008), cấp phát và quản lý bộ nhớ, the callback queue, các tính năng được thêm vào để giúp JavaScript chạy được trong môi trường bên ngoài cả trình duyệt như trên máy chủ.

Bun v1.0

Một trong số những lý do ra đời của Bun được đề cập đó là các nhà sáng lập ra muốn việc phát triển web bằng JavaScript, TypeScript trở nên dễ dàng hơn đồng thời phải đạt được tốc độ cao nhưng vẫn phải làm cho các thư viện và web framework hoạt động tốt.

Phải công nhận hệ sinh thái của JavaScript đang rất phức tạp. Có đủ loại công cụ và syntax khác nhau: common JS, ESM modules, TypeScript... Mỗi loại đòi hỏi mỗi cách setup riêng. Bun ra đời để loại bỏ sự phức tạp đó. Các file js, ts, jsx, tsx có thể chạy trực tiếp với bun mà không phải cài thêm bất cứ dependencies nào.

Cho tới thời điểm hiện tại, có 2 JavaScript runtime được nhiều người biết đến và sử dụng bao gồm:

  • Node.js: sử dụng Google Chrome V8 engine - đây là engine được dùng trong các trình duyệt họ Chromium.
  • Deno: cũng dùng V8 engine nhưng có hỗ trợ thêm TypeScript, tốc độ nhanh xử lý hơn Node.js.

Và giờ đây chúng ta có thêm Bun, được viết bằng ngôn ngữ lập trình Zig và sử dụng WebKit engine được dùng cho trình duyệt Safari của nhà Apple - engine này vốn được đánh giá là có khả năng start up nhanh và sử dụng ít bộ nhớ hơn. Bun đang mở ra tiềm năng rất lớn khi mang lại hiệu suất vượt trội hơn Node.js và Deno. Dưới đây là hình ảnh benchmark giữa Bun v1.0, Deno và Node.js. Những con số này còn cao hơn so với kết quả benchmark thời kỳ beta của Bun. Vượt hơn cả kỳ vọng ban đầu của mình.

Benchmark: Bun, Deno, Node.js

Cài đặt Bun

Việc cài đặt Bun cực kỳ dễ dàng và nhanh chóng. Các bạn chỉ cần chạy command sau là cài được trên cả Linux, Mac, WSL:

curl -fsSL https://bun.sh/install | bash

Tuy nhiên, họ lại chưa phát hành bản cài đặt dành cho Windows. 🤔 Thế nên bác nào muốn mang nó chạy trên hệ điều hành Windows thì đành "chịu chết" 😆.

Upgrade

Nếu cần nâng cấp sau khi đã cài đặt, chỉ cần chạy built-in command sau:

bun upgrade

Bun còn làm được những gì?

All-in-one Toolkit

Bun không chỉ là một JavaScript runtime. Nó đã trở thành một bộ công cụ All-In-One, sử dụng từ giai đoạn develop, test, run, bundle khi phát triển các ứng dụng JavaScript và TypeScript:

  • Bun vừa là package-manager thay thế cho NPM, Yarn, PNPM. Bun tương thích với NPM nên nó vẫn đọc file package.json như hiện tại; vẫn sử dụng node_modules. Khác một cái là Bun dùng file bun.lockb để lưu lại log cài đặt dependencies. Ngoài ra, các câu lệnh quen thuộc khi sử dụng NPM như install, add của NPM cũng vẫn dùng tương tự đối với bun. Sẽ không cần phải cài thêm các packages:
    • npm, .npmrc, package-lock.json
    • yarn, yarn.lock
    • pnpm, pnpm.lock, pnpm-workspace.yaml
    • lerna
  • Bun cũng vừa đóng vai trò làm bundler, giống vai trò của Vite, Webpack. Bun được viết để tương thích với các ESBuild và cho hiệu năng tốt hơn. Nó sẽ có thể thay thế các công cụ hiện tại như:
    • esbuild, webpack
    • parcel, .parcelrc
    • rollup, rollup.config.js
  • Bun cũng kiêm luôn vai trò test runner để chạy test cho ứng dụng giống như Jest và nó cũng tương thích với Jest. Hỗ trợ snapshot testing, mocking, code coverage. Do đó cũng sẽ không cần các dependencies sau nữa:
    • jest, jest.config.js
    • ts-jest, @swc/jest, babel-jest
    • jest-extended
    • vitest, vitest.config.ts
  • Và cuối cùng, Bun là một JavaScript runtime thay thế cho Node.js, Deno. Lúc này sẽ có một số thứ sẽ bị thay thế bao gồm:
    • node được thay thế bằng bun
    • npx được thay thế bằng bunx - nhanh hơn gấp 5 lần
    • nodemon - không cần nữa vì Bun đã có sẵn built-in watch mode
    • dotenv, cross-env -> cũng không còn cần nữa vì Bun có khả năng đọc từ file .env

⚡️⚡️⚡️ Dù kiêm nhiệm nhiều thứ nhưng khoản nào cũng cho thấy Bun có tốc độ vượt trội cả!!

Hôm trước mình cũng có làm một video ngắn so sánh tốc độ vượt trội của Bun trong vai trò bundler khi compare với NPM và PNPM. Các bạn có thể xem lại tại bài viết này https://viblo.asia/qPoL77XjLvk.

Khả năng tương thích Node.js

Một điểm đặc biệt quan trọng đó là Bun được thiết kế để có khả năng tương thích với Node.js. Nghĩa là, các ứng dụng đang chạy trên Node.js sẽ có thể chuyển qua chạy trên Bun. 😍😍😍

  • Bun vẫn cung cấp các biến global như __dirname, process.
  • Bun vẫn sử dụng node_modules giống Node.js
  • Hỗ trợ các modules có trên Node.js

Tính tới thời điểm hiện tại, Bun đã implenent khoảng 40 modules của Node.js. Một số ít module màu đỏ trong hình dưới đây là chưa được implement bao gồm: dgram, http2, inspector, replv8.

image.png

Trong thời gian beta, Bun đã được test chạy thử với các framework backend và frontend phổ biến hiện nay như Express, Koa, Hono, Vue, React... chúng đều có thể hoạt động được trên Bun.

image.png

Tốc độ

Trong một bài test nhỏ với chương trình console.log('Welcome to Dev Success 101!') mình thử nghiệm để kiểm chứng lại lời công bố của Bun trong ngày release phiên bản 1.0. Bun hoàn thành việc chạy xong script trong vòng 10ms, trong khi Node mất khoảng 40ms. Tức cũng nhanh gấp gần x4 so với Node theo lời công bố. Uy tín! 🤝

Còn dưới đây là công bố speed compare từ Bun khi chạy script hello world.

image.png

Hỗ trợ TypeScript / JSX

Như đã nói ở trên, Bun có khả năng chạy JavaScript, TypeScript và thậm chí cả JSX/TSX mà không cần cài thêm dependencies nào:

bun index.js
bun index.ts
bun index.jsx
bun index.tsx

Hỗ trợ ESM & CommonJS

Bun hỗ trợ cả ESM và CommonJS nên chúng ta cũng không phải lo lắng về các file .cjs, .mjs, hay thêm "type": "module" vào package.json như trên Node.js. Tất cả syntax của các file trên đều có thể dùng chung. Nghĩa là trong cùng một file vẫn có thể dùng đồng thời cả import và require.

import lodash from "lodash";
const _ = require("underscore");

WebAPIs

Bản thân Bun hỗ trợ các Web APIs đang có trên browser như: fetch, Request, Response, WebSocket, and ReadableStream. Các built-in Web APIs được triển khai trong Bun dưới dạng native-code chứ không phải javascript-code nên sẽ nhanh hơn và tin cậy hơn. Do đó, không cần phải cài thêm các package như node-fetchws nữa:

const response = await fetch("https://example.com/");
const text = await response.text();

Hot reloading

Bun hỗ trợ hot-reloading bằng cách thêm option --hot. Ví dụ:

bun --hot server.ts

Cái hay của Bun so với tính năng tương tự trên nodemon đó là nó thực hiện hot reload mà không tắt tiến trình cũ. Điều này làm cho các kết nói HTTP, WebSocket không bị disconnect hoặc là mất state trong quá trình test. 😍😍😍

Plugins

Bun được thiết kế để có thể tạo ra sự tùy biến cao highly-customizable nên nó cung cấp Plugin API (giống esbuild). Và như đã đề cập khi nói về vai trò bundler, Bun tương thích với ESBuild nên plugin của ESBuild có thể hoạt động được với Bun. Dưới đây là ví dụ một plugin có thể can thiệp vào quá trình load các file yamlpng.

import { plugin } from "bun";

plugin({
  name: "YAML",
  async setup(build) {
    const { load } = await import("js-yaml");
    const { readFileSync } = await import("fs");
    build.onLoad({ filter: /.(yaml|yml)$/ }, (args) => {
      const text = readFileSync(args.path, "utf8");
      const exports = load(text) as Record<string, any>;
      return { exports, loader: "object" };
    });
  },
});

Bun APIs

Ngoài việc hướng đến tương thích với Node.js bằng cách cung cấp các Node APIs, Bun cũng có những native APIs được tối ưu hóa của riêng nó. Các API này hướng đến tốc độ xử lý nhanh và dễ sử dụng. Dưới đây là một số APIs được Bun cung cấp mà chúng ta có thể sử dụng luôn mà không phải cài thêm dependencies nào.

Bun.file()

Sử dụng để đọc một file, trả về có kiểu BunFile là mở rộng của File trong Web APIs. API này được cho là nhanh hơn gấp 10 lần với Node.js.

const file = Bun.file("package.json");
const contents = await file.text();

Nó cung hỗ trợ nhiều kiểu định dạng của dữ liệu trả về khác nhau như:

const file = Bun.file("package.json");

await file.text();          // '{ "name":"DevSuccess101" }'
await file.arrayBuffer();   // ArrayBuffer(2)
await file.blob();          // Blob
await file.stream();        // ReadableStream
await file.json();          // { name: "DevSuccess101" }

Bun.write()

Vừa nãy là đọc file. Còn bây giờ là ghi dữ liệu ra file. API Bun.write() được cho là nhanh hơn gấp 3 lần so với Node.js.

await Bun.write("index.html", "<html/>");
await Bun.write("index.html", Buffer.from("<html/>"));
await Bun.write("index.html", Bun.file("home.html"));
await Bun.write("index.html", await fetch("https://devsuccess101.com/"));

Bun.serve()

Dựng HTTP server và WebSocket server nhanh chóng bằng một câu lệnh duy nhất Bun.serve():

Bun.serve({
  port: 3000,
  // HTTP handler
  fetch(request) {
    return new Response("Hello from Bun!");
  },
  // WebSocket handler
  websocket: {
    open(ws) { ... },
    message(ws, data) { ... },
    close(ws, code, reason) { ... },
  }
});
  • Bun.serve() đáp ứng được số lượng HTTP request / giây lớn hơn 4 lần so với Node.js.
  • Bun.serve() đáp ứng được số lượng WebSocket messages / giây lớn hơn 5 lần so với Node.js.

bun:sqlite

Một built-in API rất thú vị của Bun đó là hỗ trợ SQLite. Built-in API này cũng được làm giống với better-sqlite3. Ngoài ra, nó cũng được viết bằng native code để nhanh hơn 4 lần khi so với dùng better-sqlite3 trên Node.js.

import { Database } from "bun:sqlite";

const db = new Database(":memory:");
const query = db.query("select 'Bun' as runtime;");
query.get(); // => { runtime: "Bun" }

Bun.password

Bun.password là API dùng để hash và verify password bằng các thuật toán băm mạnh mẽ bcryptargon2.

const password = "super-secure-pa$$word";
const hash = await Bun.password.hash(password);
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh...

const isMatch = await Bun.password.verify(password, hash);
// => true

bun:test

Đây là built-in test runner được làm sẵn bởi Bun.

import { test, expect } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});
bun test

Như đã đề cập, nó tương thích với Jest nên nếu đang viết test cases với Jest thì bạn không cần phải lo lắng vì việc migrate khá dễ dàng vì những thứ import từ @jest/globals sẽ được tự map sang bun:test. :magic:

import { test } from "@jest/globals";

describe("test suite", () => {
  // ...
});

Khi chạy test, Bun cho tốc độ kiểm tra:

  • Nhanh hơn 13 lần so với Jest
  • Nhanh hơn 8 lần so với Vitest
  • Đặc biệt, phần triển khai function expect().toEqual() trên Bun chạy nhanh hơn 100 lần so với Jest và 10 lần so với Vitest.

image.png

Node.js liệu sẽ chết??

Sau khi dùng thử qua các phiên bản beta, bằng những khả năng vượt trội về hiệu năng mà Bun mang lại thì mình cũng đánh giá rất cao về nó cũng như là cho thấy tiềm năng ứng dụng Bun vào production cho các dự án thay thế Node.js để cải thiện hiệu năng.

Tuy nhiên...

Mặc dù Bun có nhiều thứ điểm ưu việt, tích hợp nhiều thứ như vậy nhưng cá nhân mình nghĩ Bun vẫn sẽ cần một khoảng thời gian dài nữa để chứng minh bản thân cũng như tạo ra được một chỗ đứng nhất định trong lĩnh vực phát triển web. Điều này có thể sẽ cần đến nhiều năm nữa.

Bởi lẽ theo mình:

  1. Node.js đã ăn sâu vào trong thị trường phát triển web hiện đại. Rất khó để lôi kéo mọi người chuyển đổi sang dùng Bun ngay lập tức. Không ai muốn từ bỏ một service đang hoạt động ổn định trên production để chuyển sang dùng một thứ vừa mới release phiên bản 1.0.
  2. Bun đã release phiên bản 1.0 - production ready đầu tiên. Đại đa số mọi người vẫn cần thời gian để theo dõi tính ổn định của nó. Bản thân Bun sau khi release cũng có nhiều lỗi được phát hiện và đang tiến hành fix.
  3. Mặc dù nói là tương thích Node.js nhưng không phải là tất cả các APIs của Node đều được hỗ trợ. Chúng ta cũng không loại trừ khả năng sẽ còn những module của Node.js không được implement trên Bun. Điều này có nghĩa là sẽ có những dự án Node.js không thể chuyển sang Bun ngay được. Chẳng hạn, module HTTP/2 chưa được implement nên nếu dự án mà bạn muốn dùng gRPC thì chắc chắn là không phù hợp vì
  4. Không phải toàn bộ các framework / tool trên thị trường có thể chạy mượt với Bun. Mình là một fan của Cloudflares, một số thứ như Cloudflare Pages / Workers mình có setup với Bun thì vẫn chưa thể hoạt động ngay được. Nhìn rộng ra thì vẫn có thể còn nhiều công cụ khác cũng có thể như vậy.
  5. Nhu cầu tuyển dụng của thị trường vẫn còn chưa có, sẽ dẫn tới Bun sẽ chưa thể thu hút nhiều lập trình viên sử dụng.

Vậy những dự án như nào sẽ sử dụng Bun?

Mình thấy, Bun hoàn toàn có thể ứng dụng vào các service nhỏ nhỏ với mục đích thử nghiệm ngay từ bây giờ để kiểm chứng performance mà Bun công bố trước khi tự tin ứng dụng nó vào trong các sản phẩm quy mô lớn hơn sau này. Không thể phủ nhận rằng chỉ trọng một thời gian ngắn mà Bun có thể làm được nhiều thứ đến vậy! Nhanh đến vậy!

Tổng kết

Trên đây là những thông tin mới nhất về Bun mà mình muốn chia sẻ tới mọi người. Một số đánh giá trong bài cũng là những suy nghĩ thiên kiến cá nhân của mình. Còn các bạn thì sẽ nghĩ sao về Bun? Hãy comment phía dưới để chúng ta cũng thảo luận nhé!


Mọi người ủng hộ mình bằng cách giúp mình một lượt like và subscribe cho kênh Dev Success 101 trên các trang bạn yêu thích như Viblo / YouTube / TikTok với nhé. Đây là các kênh mà mình mới tạo nên rất cần sự ủng hộ từ mọi người. Mình rất cảm ơn tất cả các bạn đã đón đọc.

✴️ Subscribe kênh! https://l.devsuccess101.com/subscribe
✴️ Join our Discord community for help https://l.devsuccess101.com/discord
✴️ Donate: Momo, Paypal

✴️ TikTok: https://l.devsuccess101.com/tiktok
✴️ YouTube: https://l.devsuccess101.com/youtube
✴️ Viblo: https://l.devsuccess101.com/viblo
✴️ Facebook: https://l.devsuccess101.com/facebook
✴️ Discord: https://l.devsuccess101.com/discord


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí