Bạn đã bao giờ mơ ước tạo ra một ứng dụng siêu gọn gàng, chỉ là một file "chạy cái ăn ngay" mà không cần khách hàng cài đặt lằng nhằng hay bạn phải "show" hết code ra không? Nếu có, thì bạn đã tìm đúng chỗ rồi đấy! Đây là câu chuyện về cách chúng ta kết hợp Vite, Express và Bun để tạo ra một "viên đạn" phần mềm tự chứa, đảm bảo bí mật code và sự tiện lợi tối đa.1. Tại sao lại phải "xoắn"?Thường thì khi bạn phát triển một tool nội bộ bằng JavaScript (hoặc một dự án cho khách hàng), việc phân phối nó có thể khá rắc rối. Bạn muốn khách hàng chỉ cần "nhấp đúp" là chạy, không phải lo cài Bun, cài thư viện hay gỡ lỗi gì sất. Đồng thời, bạn cũng muốn giữ code của mình an toàn, không muốn nó "lộ thiên". Mục tiêu là một file binary duy nhất, tự chứa 100%. Nghe hấp dẫn đúng không? <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/single_binary.png' alt='Mô tả file binary tự chứa'>2. Bun Bundler/Compiler: Anh tài nhưng đôi khi "chập cheng"Bun, "người hùng" mới nổi trong giới JavaScript, làm rất tốt công việc biên dịch dự án thành file thực thi (executable). Bạn có thể xem tài liệu của Bun về tính năng này ở đây: https://bun.sh/docs/bundler/executables. Tuy nhiên, Bun có một "điểm yếu" nhỏ khi nói đến việc đóng gói các file tĩnh (như giao diện người dùng) để sau đó có thể phục vụ cục bộ. Tính năng `embed dir` của Bun hiện tại vẫn còn trong giai đoạn "beta", đôi khi nó hoạt động không ổn định và rất khó để lấy lại các file đã nhúng để phục vụ bằng Express. Vậy là chúng ta cần một "bí kíp" khác! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/bun_logo.png' alt='Logo Bun'>3. Giải pháp "đỉnh của chóp": Hệ thống Tệp Ảo (Virtual File System - VFS)!Bí quyết ở đây là tạo ra một "Hệ thống Tệp Ảo" (VFS). Tưởng tượng bạn có cả một thư mục chứa đầy đủ "đồ đạc" của ứng dụng frontend sau khi build (thư mục `/dist`). Thay vì để nguyên chúng nằm rải rác, chúng ta sẽ "đóng gói" tất cả vào một file JavaScript duy nhất, biến chúng thành một "kho báu" mà server có thể truy cập ngay lập tức! Bước 1: Build dự án Vite của bạn Đầu tiên, hãy để Vite làm công việc của nó, biên dịch dự án frontend của bạn thành các file tĩnh gọn gàng trong thư mục `/dist`.`vite build` Bước 2: Biến thư mục `/dist` thành một hệ thống tệp ảo "độc nhất vô nhị"!Chúng ta sẽ dùng một công cụ đặc biệt để lấy toàn bộ thư mục `/dist` và đóng gói nó lại thành một file `.js` duy nhất. Bạn có thể nghĩ đây là một file khá "nặng", nhưng đừng lo lắng! Vì chúng ta sẽ đóng gói tất cả vào một file binary duy nhất, nên kích thước file không phải là vấn đề lớn.Trước đây, `make-vfs` (https://github.com/seveibar/make-vfs/) là một lựa chọn:`bunx make-vfs --dir ./dist --content-format string --outfile ./bundle/client-bundle-vfs.js` Cập nhật quan trọng: `make-vfs` có vẻ đã "dở chứng" với cơ chế mã hóa/giải mã Base64 của nó. Vì vậy, người viết bài này đã tự tay "chế" ra một giải pháp thay thế mới toanh! Bạn có thể tìm thấy nó ở đây: https://gist.github.com/calumk/65d2b9352c92e7e83cbb59fbb205f123. Hãy sử dụng giải pháp này để đảm bảo mọi thứ "ngon lành cành đào" nhé! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/vfs_diagram.png' alt='Sơ đồ hệ thống tệp ảo VFS'> Bước 3: Dùng Express để "khai thác" và phục vụ VFS của bạn:Giờ đây, chúng ta có file VFS chứa tất cả các file frontend. Làm sao để Express có thể "hiểu" và phục vụ chúng đây? Đơn giản thôi! Chúng ta sẽ "nhập" file VFS này vào server Express của mình.```javascriptimport staticRoutes from "../../dist/client-bundle-vfs.js";import { lookup } from "es-mime-types";import express from "express";import routes from "./routes.js";const app = express();// Phục vụ các API routes của Expressapp.use(routes);// Phục vụ các file tĩnh đã được đóng gói (từ VFS)app.use((req, res, next) => { const url = new URL(req.url, `http://${req.headers.host}`); const path = url.pathname; const normalizedPath = path.replace(/^\//, "").replace(/\/$/, ""); // Loại bỏ / ở đầu/cuối // Kiểm tra xem đường dẫn có tồn tại trong VFS không if (staticRoutes[normalizedPath]) { let mimeType = lookup(normalizedPath); // Xác định loại MIME (ví dụ: text/html, image/png) return res.status(200).type(mimeType).send(staticRoutes[normalizedPath]); } next(); // Nếu không tìm thấy, chuyển sang middleware tiếp theo});// Fallback cho Vue Router (để các đường dẫn SPA hoạt động)app.use((req, res) => { return res.status(200).type("text/html").send(staticRoutes["index.html"]);});app.listen(3002, () => { console.log("Server đang chạy tại http://localhost:3002");});```Trong đoạn code trên, chúng ta dùng một `middleware` của Express để "đón lõng" các yêu cầu. Mỗi khi có yêu cầu đến, nó sẽ kiểm tra xem đường dẫn đó có tồn tại trong đối tượng `staticRoutes` (chính là VFS của chúng ta) không. Nếu có, nó sẽ xác định loại tệp (ví dụ: HTML, CSS, JS, hình ảnh) và gửi nội dung từ VFS đi. Đoạn `app.use((req, res) => { ... })` cuối cùng là một "pha cứu thua" cho Vue Router, đảm bảo mọi đường dẫn trong ứng dụng SPA của bạn đều trỏ về `index.html` nếu không tìm thấy file cụ thể. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/express_server.png' alt='Express server phục vụ nội dung'> Bước 4: Đóng gói toàn bộ ứng dụng của bạn vào một file Binary duy nhất!Giờ thì chúng ta có server Express đã "ôm" cả file VFS vào lòng. Bước cuối cùng là dùng Bun để biên dịch tất cả thành một file thực thi độc lập!`bun build ./src/server/main-compile.js --compile --outfile myServer` Lệnh này sẽ biến toàn bộ dự án của bạn (bao gồm cả server Express và các file frontend đã được đóng gói trong VFS) thành một file `myServer` duy nhất mà bạn có thể phân phối cho bất kỳ ai! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/final_build.png' alt='Quá trình build cuối cùng thành file binary'>4. Tham khảo dự án mẫu trên GitHub:Nếu bạn muốn "mắt thấy tai nghe" (hay đúng hơn là "tay code"), hãy ghé thăm kho lưu trữ GitHub của tác giả nhé: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg' alt='Logo GitHub'> https://github.com/calumk/vite-express-bun-compiled Dự án `vite-express-bun-compiled` này là một ví dụ tuyệt vời về cách biên dịch một ứng dụng Vite-Vue sử dụng VFS. Nó là một template đơn giản để bạn bắt đầu với Vue 3, Vite, Bun và VFS, giúp bạn đóng gói ứng dụng Vue 3 của mình và chạy nó bằng Bun, đồng thời quản lý file dễ dàng với VFS. Điều này cho phép ứng dụng của bạn chạy độc lập mà không cần server riêng biệt, giúp việc triển khai và chia sẻ trở nên cực kỳ tiện lợi!
Chia sẻ hành trình xây dựng website cá nhân từ A đến Z, những bài học xương máu khi làm việc với AI (ChatGPT, GitHub Copilot), và bí quyết biến ý tưởng thành hiện thực.
Khám phá những công nghệ Front-end dẫn đầu xu hướng năm 2025 như React, Next.js, Tailwind CSS, Vite và Svelte. Nắm bắt các framework và công cụ không thể thiếu để phát triển web hiện đại.