Build Desktop App “siêu thực”: Khi Tauri và React hạ gục cơn ác mộng ngốn RAM của Electron

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Cú shock lúc 2 giờ sáng: Khi ứng dụng “Hello World” nuốt chửng RAM

Đúng 2 giờ sáng, điện thoại mình rung bần bật. Client nhắn tin phàn nàn dashboard nội bộ vừa bàn giao khiến máy tính của nhân viên kế toán… đóng băng hoàn toàn. Mở Task Manager lên, mình sững sờ. Một ứng dụng đơn giản chỉ hiển thị biểu đồ và bảng dữ liệu lại đang “ngốn” tới 1.5GB RAM. Thủ phạm không ai khác chính là Electron.

Vấn đề này vốn chẳng của riêng ai. Electron cực kỳ tiện lợi vì cho phép dùng HTML/CSS/JS để làm app Desktop. Tuy nhiên, cái giá phải trả là mỗi bản build đều phải gánh theo cả bộ engine Chromium và Node.js runtime. Một ứng dụng trống trơn vừa khởi động đã chiếm vài trăm MB RAM là chuyện thường tình. File cài đặt nặng gần 100MB cũng không còn là điều xa lạ.

Tại sao Electron lại nặng nề đến thế?

Hãy nhìn vào cách Electron vận hành để thấy rõ vấn đề. Mỗi khi chạy một app Electron, máy tính sẽ khởi tạo một instance của Chromium. Cứ tưởng tượng bạn mở 10 ứng dụng khác nhau như Slack, Discord, VS Code hay Spotify. Lúc đó, hệ thống đang phải gánh 10 trình duyệt Chrome chạy ngầm. Thật là một sự lãng phí tài nguyên khủng khiếp.

Chưa kể, việc đóng gói toàn bộ source code Node.js cùng mớ node_modules khổng lồ khiến kích thước ứng dụng phình to nhanh chóng. Với các dự án đòi hỏi sự tinh gọn và bảo mật, Electron bắt đầu lộ rõ những điểm yếu chí mạng mà không bản cập nhật nào có thể xử lý triệt để.

Đi tìm sự cân bằng giữa tốc độ và tính tiện dụng

Đứng trước bài toán này, mình đã cân nhắc ba hướng đi chính:

  • Native (C++/C#): Hiệu năng đỉnh cao, app siêu nhẹ nhưng thời gian phát triển quá lâu. Việc thiết kế giao diện (UI) phức tạp bằng CSS cũng trở nên xa xỉ.
  • Python (PyQt/PySide): Dễ viết hơn C++, nhưng đóng gói (packaging) lại là cơn ác mộng. Lỗi dependency xảy ra như cơm bữa trên máy người dùng cuối.
  • Tauri: Một “tân binh” đang làm mưa làm gió. Nó dùng Rust làm lõi xử lý hệ thống và cho phép sử dụng bất kỳ Web framework nào như React, Vue hay Svelte để làm UI.

Tauri – Giải pháp “nhẹ tựa lông hồng”, mạnh mẽ như Rust

Sau vài bài test thực tế, mình quyết định chọn Tauri. Khác biệt lớn nhất là Tauri không đi kèm Chromium. Thay vào đó, nó tận dụng WebView có sẵn của hệ điều hành (WebView2 trên Windows, WebKit trên macOS/Linux). Kết quả thu được thật kinh ngạc: File cài đặt từ 80MB giảm xuống chỉ còn 4MB. Lượng RAM tiêu thụ cũng giảm từ 1.5GB xuống dưới 100MB cho cùng một chức năng.

Bước 1: Chuẩn bị môi trường (Đừng bỏ qua!)

Vì Tauri chạy trên nền Rust, bạn cần cài đặt Rust compiler trước tiên. Đừng quá lo lắng, bạn không cần phải là chuyên gia Rust để có thể xây dựng ứng dụng.

# Cài đặt Rust (Windows/Mac/Linux) thông qua rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Đừng quên kiểm tra Node.js để sẵn sàng chạy React:

node -v
npm -v

Bước 2: Khởi tạo dự án Tauri + React

Để tiết kiệm thời gian, mình thường dùng lệnh tạo nhanh thay vì cấu hình thủ công:

npm create tauri-app@latest

Terminal sẽ đưa ra vài lựa chọn. Hãy chọn TypeScript / JavaScript, sau đó chọn React kết hợp với Vite để đạt tốc độ build nhanh nhất có thể.

Bước 3: Giải mã cấu trúc thư mục

Sau khi khởi tạo, bạn sẽ thấy thư mục src-tauri. Đây là nơi chứa code Rust – đóng vai trò là backend xử lý các tác vụ nặng hoặc can thiệp sâu vào hệ thống. Thư mục src vẫn là nơi bạn viết code React như bình thường.

Trong quá trình code giao diện, nếu cần xử lý các chuỗi JSON phức tạp từ API, mình thường dùng các công cụ tại toolcraft.app. Ví dụ, công cụ JSON Formatter giúp test nhanh dữ liệu mà không cần cài thêm extension rườm rà vào VS Code.

Bước 4: Xây dựng tính năng “Bridge”

Điểm ăn tiền nhất của Tauri chính là cơ chế gọi hàm Rust từ JavaScript (Commands). Nếu muốn đọc một file trong máy tính, hãy để Rust xử lý thay vì dùng Node.js để đảm bảo an toàn và hiệu năng.

Trong file src-tauri/src/main.rs:

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Chào {}, app này chỉ 4MB, mở app mất chưa tới 2 giây!", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("lỗi khi chạy ứng dụng tauri");
}

Bên phía React (App.tsx), việc gọi hàm này cực kỳ đơn giản:

import { invoke } from '@tauri-apps/api/tauri';

const handleGreet = async () => {
  const message = await invoke('greet', { name: 'Thành' });
  console.log(message);
};

Bước 5: Build và tận hưởng thành quả

Khi mọi thứ đã sẵn sàng, hãy chạy lệnh build để tạo file cài đặt hoàn chỉnh:

npm run tauri build

Hãy kiểm tra thư mục src-tauri/target/release/bundle. Bạn sẽ thấy file .msi hoặc .dmg nhỏ gọn đến khó tin. Cảm giác lúc đó thực sự giống như vừa trút bỏ được gánh nặng ngàn cân vậy.

Kết luận: Khi nào nên chọn Tauri?

Tauri rất mạnh, nhưng không phải là liều thuốc vạn năng cho mọi trường hợp. Nếu app cần can thiệp sâu vào tính năng riêng của Chromium hoặc bạn muốn dùng 100% Node.js ở backend mà không đụng vào Rust, Electron vẫn là lựa chọn an toàn.

Dù vậy, với xu hướng tối ưu hiệu năng hiện nay, Tauri rõ ràng là một đối thủ đáng gờm. Nó giúp chúng ta tạo ra những ứng dụng vừa đẹp nhờ React, vừa nhanh và an toàn nhờ Rust. Nếu bạn đang ấp ủ một dự án Desktop app mới, hãy thử Tauri một lần. Chắc chắn bạn sẽ không muốn quay lại với Electron nữa đâu.

Share: