Tauri v2 — Xây dựng Desktop App siêu nhẹ với Vue.js và Rust
Posted on: 4/26/2026 10:12:47 PM
Table of contents
- Kiến trúc Tauri v2
- Tại sao không phải Electron?
- Khởi tạo dự án Tauri + Vue.js
- IPC — Giao tiếp giữa Vue và Rust
- Bảo mật Capability-Based
- Plugin System
- Mobile Support — Cùng codebase cho Desktop và Mobile
- Auto-Updater với Delta Updates
- Production Workflow
- Hiệu năng thực tế — Benchmark
- Khi nào nên chọn Tauri v2?
- Lộ trình phát triển
- Kết luận
Nếu bạn từng dùng Electron để xây ứng dụng desktop, hẳn bạn đã quen với việc ship cả một trình duyệt Chromium nặng hơn 100 MB chỉ để hiển thị UI. Tauri v2 đưa ra cách tiếp cận hoàn toàn khác: sử dụng WebView có sẵn trên hệ điều hành kết hợp với backend viết bằng Rust — ngôn ngữ nổi tiếng về hiệu năng và an toàn bộ nhớ. Kết quả là ứng dụng desktop (và cả mobile) chỉ từ 3-8 MB, khởi động dưới nửa giây.
Kiến trúc Tauri v2
Tauri sử dụng mô hình multi-process tương tự trình duyệt, nhưng gọn nhẹ hơn nhiều. Có hai thành phần chính:
graph TB
subgraph "Tauri Core (Rust)"
A["tauri::App"] --> B["Window Manager
(TAO)"]
A --> C["WebView Renderer
(WRY)"]
A --> D["Plugin System"]
A --> E["IPC Bridge"]
D --> F["fs / dialog / shell
http / notification
updater / sql"]
end
subgraph "Frontend (Vue.js)"
G["Vue Components"] --> H["@tauri-apps/api"]
H --> E
end
subgraph "OS Layer"
C --> I["WebView2
(Windows)"]
C --> J["WKWebView
(macOS/iOS)"]
C --> K["WebKitGTK
(Linux)"]
B --> L["Native Window"]
end
style A fill:#e94560,stroke:#fff,color:#fff
style G fill:#42b883,stroke:#fff,color:#fff
style I fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style J fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style K fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
Kiến trúc tổng quan Tauri v2 — Rust core + WebView hệ thống + Frontend framework
- TAO — thư viện quản lý cửa sổ (window creation, event loop, system tray) viết bằng Rust, hỗ trợ Windows/macOS/Linux/Android/iOS.
- WRY — lớp abstraction cho WebView, tự động chọn WebView2 trên Windows, WKWebView trên macOS, WebKitGTK trên Linux. Không bundle Chromium → tiết kiệm >100 MB.
- IPC Bridge — giao tiếp JavaScript ↔ Rust qua hàm
invoke(), serialize/deserialize tự động với serde.
Tại sao không phải Electron?
| Tiêu chí | Tauri v2 | Electron |
|---|---|---|
| Bundle size | 3–8 MB | 80–200 MB |
| RAM khi idle | 30–50 MB | 200–300 MB |
| Startup time | < 0.5 giây | 1–2 giây |
| Backend | Rust (memory-safe) | Node.js |
| Renderer | System WebView | Bundled Chromium |
| Mobile support | iOS + Android (v2) | Không hỗ trợ |
| Security model | Capability-based (deny by default) | Permissive (phải tự lock down) |
| Auto-updater | Built-in, delta updates | electron-updater (third-party) |
Khi nào vẫn nên chọn Electron?
Nếu ứng dụng cần rendering pixel-perfect đồng nhất trên mọi OS (như Figma, Spotify), hoặc phụ thuộc nặng vào Node.js ecosystem và native modules, Electron vẫn là lựa chọn hợp lý. Tauri phù hợp hơn cho internal tools, utilities, và ứng dụng cần bundle nhỏ + bảo mật chặt.
Khởi tạo dự án Tauri + Vue.js
Tauri hỗ trợ mọi frontend framework, nhưng Vue.js là một trong những lựa chọn phổ biến nhất nhờ hệ sinh thái phong phú và Vite build siêu nhanh.
# Tạo project mới với Vue + TypeScript
npm create tauri-app@latest my-app -- --template vue-ts
# Cấu trúc thư mục
my-app/
├── src/ # Vue frontend
│ ├── App.vue
│ ├── main.ts
│ └── components/
├── src-tauri/ # Rust backend
│ ├── src/
│ │ ├── main.rs # Entry point
│ │ └── lib.rs # Commands
│ ├── Cargo.toml
│ ├── tauri.conf.json
│ └── capabilities/ # Permission declarations
└── package.json
IPC — Giao tiếp giữa Vue và Rust
Cốt lõi sức mạnh của Tauri nằm ở IPC (Inter-Process Communication). Frontend gọi hàm Rust qua invoke(), Rust xử lý logic nặng rồi trả kết quả. Mọi thứ đều type-safe nhờ serde.
// src-tauri/src/lib.rs
use tauri::command;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SystemInfo {
pub cpu_usage: f64,
pub memory_total: u64,
pub memory_used: u64,
pub os_name: String,
}
#[command]
async fn get_system_info() -> Result<SystemInfo, String> {
// Logic Rust chạy native, không qua V8/Node.js
Ok(SystemInfo {
cpu_usage: 23.5,
memory_total: 16_000_000_000,
memory_used: 8_500_000_000,
os_name: std::env::consts::OS.to_string(),
})
}
#[command]
async fn process_file(path: String) -> Result<String, String> {
let content = std::fs::read_to_string(&path)
.map_err(|e| e.to_string())?;
// Xử lý file với hiệu năng Rust native
Ok(format!("Processed {} bytes", content.len()))
}
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
get_system_info,
process_file
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
<!-- src/components/SystemMonitor.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { invoke } from '@tauri-apps/api/core'
interface SystemInfo {
cpu_usage: number
memory_total: number
memory_used: number
os_name: string
}
const info = ref<SystemInfo | null>(null)
onMounted(async () => {
// Gọi hàm Rust từ Vue — type-safe, async
info.value = await invoke<SystemInfo>('get_system_info')
})
async function handleFile(path: string) {
const result = await invoke<string>('process_file', { path })
console.log(result)
}
</script>
<template>
<div v-if="info">
<p>OS: {{ info.os_name }}</p>
<p>CPU: {{ info.cpu_usage }}%</p>
<p>RAM: {{ (info.memory_used / 1e9).toFixed(1) }} /
{{ (info.memory_total / 1e9).toFixed(1) }} GB</p>
</div>
</template>
Type-safety end-to-end
Rust struct được serialize qua serde → JSON → TypeScript interface. Nếu bạn dùng tauri-specta, type bindings được tự động generate từ Rust sang TypeScript, đảm bảo frontend và backend luôn đồng bộ.
Bảo mật Capability-Based
Đây là điểm khác biệt lớn nhất với Electron. Tauri v2 áp dụng nguyên tắc deny by default — mọi API hệ thống đều bị chặn cho đến khi được khai báo rõ ràng trong file capability.
graph LR
A["Vue Component
invoke('read_file')"] --> B{"Capability
Check"}
B -->|"Allowed"| C["Rust Command
thực thi"]
B -->|"Denied"| D["Error:
Permission Denied"]
C --> E["Trả kết quả
về Frontend"]
style A fill:#42b883,stroke:#fff,color:#fff
style B fill:#ff9800,stroke:#fff,color:#fff
style C fill:#4CAF50,stroke:#fff,color:#fff
style D fill:#e94560,stroke:#fff,color:#fff
Luồng kiểm tra permission trong Tauri v2
// src-tauri/capabilities/main.json
{
"identifier": "main-capability",
"description": "Permissions cho cửa sổ chính",
"windows": ["main"],
"permissions": [
"core:default",
{
"identifier": "fs:allow-read-text-file",
"allow": [
{ "path": "$APPDATA/**" },
{ "path": "$DOCUMENT/**" }
]
},
"dialog:allow-open",
"notification:default"
]
}
Mỗi cửa sổ (window) có capability riêng. Một cửa sổ settings có thể truy cập file system, nhưng cửa sổ webview hiển thị nội dung bên ngoài thì không. Điều này giúp tuân thủ SOC 2, HIPAA dễ dàng hơn vì attack surface được thu hẹp tối đa.
Plugin System
Tauri v2 có hệ thống plugin phong phú, chia thành official plugins (do Tauri team duy trì) và community plugins:
| Plugin | Chức năng | Use case |
|---|---|---|
@tauri-apps/plugin-fs |
Đọc/ghi file system | Text editor, file manager |
@tauri-apps/plugin-dialog |
Native file dialog, message box | Open/Save file picker |
@tauri-apps/plugin-http |
HTTP client (bypass CORS) | API calls, download |
@tauri-apps/plugin-notification |
System notification | Alert, reminder |
@tauri-apps/plugin-updater |
Auto-update + delta updates | Silent app updates |
@tauri-apps/plugin-sql |
SQLite / MySQL / PostgreSQL | Local database |
@tauri-apps/plugin-store |
Key-value persistent storage | Settings, preferences |
@tauri-apps/plugin-shell |
Chạy command/script | CLI wrapper, automation |
Mobile Support — Cùng codebase cho Desktop và Mobile
Tauri v2 mang tới khả năng build cho iOS và Android từ cùng một codebase. Frontend Vue.js được render trên WKWebView (iOS) hoặc Android WebView, backend Rust compile sang target tương ứng.
# Thêm mobile target
npm run tauri android init
npm run tauri ios init
# Build và chạy trên thiết bị/emulator
npm run tauri android dev
npm run tauri ios dev
Lưu ý về Mobile
Mobile support trong Tauri v2 vẫn đang hoàn thiện. Không phải tất cả plugin đều hỗ trợ mobile. Với các ứng dụng mobile phức tạp cần native UI sâu (camera, sensor, map), React Native hoặc Flutter vẫn phù hợp hơn. Tauri mobile lý tưởng cho ứng dụng dạng content, dashboard, hoặc utility.
Auto-Updater với Delta Updates
Tauri tích hợp sẵn cơ chế cập nhật ứng dụng — không cần thư viện bên thứ ba. Đặc biệt, nó hỗ trợ delta updates: chỉ tải phần thay đổi (1-5 MB) thay vì toàn bộ app.
// src-tauri/src/lib.rs — Cấu hình updater
use tauri_plugin_updater::UpdaterExt;
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
let updater = handle.updater().unwrap();
if let Some(update) = updater.check().await.unwrap() {
println!("Update available: {}", update.version);
update.download_and_install(|_, _| {}, || {}).await.unwrap();
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Production Workflow
graph LR
A["npm run build
(Vite → dist/)"] --> B["cargo build
--release"]
B --> C{"Target OS"}
C --> D["Windows
.msi / .exe
~5 MB"]
C --> E["macOS
.dmg / .app
~4 MB"]
C --> F["Linux
.deb / .AppImage
~6 MB"]
C --> G["Android
.apk
~8 MB"]
C --> H["iOS
.ipa"]
style A fill:#42b883,stroke:#fff,color:#fff
style B fill:#e94560,stroke:#fff,color:#fff
style D fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style E fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style F fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style G fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style H fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
Pipeline build đa nền tảng của Tauri v2
# Build cho production
npm run tauri build
# Với CI/CD (GitHub Actions)
# Tauri cung cấp sẵn action: tauri-apps/tauri-action
# Tự động build cho Windows, macOS, Linux và tạo release
Hiệu năng thực tế — Benchmark
graph LR
subgraph "Bundle Size"
A1["Tauri: 5 MB"] ---|vs| A2["Electron: 150 MB"]
end
subgraph "RAM Usage"
B1["Tauri: 40 MB"] ---|vs| B2["Electron: 250 MB"]
end
subgraph "Startup"
C1["Tauri: 0.3s"] ---|vs| C2["Electron: 1.5s"]
end
style A1 fill:#4CAF50,stroke:#fff,color:#fff
style A2 fill:#e94560,stroke:#fff,color:#fff
style B1 fill:#4CAF50,stroke:#fff,color:#fff
style B2 fill:#e94560,stroke:#fff,color:#fff
style C1 fill:#4CAF50,stroke:#fff,color:#fff
style C2 fill:#e94560,stroke:#fff,color:#fff
So sánh hiệu năng Tauri v2 vs Electron (ứng dụng todo-app tương đương)
Các con số trên đến từ benchmark cộng đồng với ứng dụng có chức năng tương đương. Sự chênh lệch chủ yếu do Tauri không bundle Chromium (~120 MB) và không chạy Node.js runtime (~50 MB RAM overhead).
Khi nào nên chọn Tauri v2?
| Phù hợp | Cân nhắc thêm |
|---|---|
| Internal tools, dashboard, admin panel | App cần pixel-perfect cross-OS rendering |
| CLI wrapper với GUI đẹp | Phụ thuộc nặng vào Node.js native modules |
| Utility app (converter, editor nhẹ) | Team chưa quen Rust (learning curve) |
| App cần bảo mật cao (fintech, healthcare) | Cần hệ sinh thái plugin rộng như Electron |
| Cross-platform desktop + mobile từ 1 codebase | Mobile app phức tạp cần native UI sâu |
Mẹo cho team đã biết Vue.js
Nếu team đã có kinh nghiệm Vue.js + Vite, chi phí chuyển sang Tauri rất thấp — frontend code gần như giữ nguyên. Phần Rust backend chỉ cần viết các command đơn giản ban đầu, sau đó mở rộng dần khi cần tương tác sâu với hệ thống.
Lộ trình phát triển
Kết luận
Tauri v2 không chỉ là một "Electron nhẹ hơn" — nó đại diện cho một triết lý thiết kế khác: tận dụng tối đa những gì hệ điều hành đã có thay vì bundle mọi thứ. Với Rust backend an toàn bộ nhớ, WebView hệ thống, security model chặt chẽ, và khả năng target cả desktop lẫn mobile, Tauri v2 là lựa chọn đáng cân nhắc cho mọi developer Vue.js muốn đưa ứng dụng web ra khỏi trình duyệt.
Với bản 2.4.2 stable hiện tại và cộng đồng đang phát triển nhanh, đây là thời điểm tốt để bắt đầu tìm hiểu và thử nghiệm Tauri cho dự án tiếp theo.
Tham khảo
Local-First Architecture — Khi ứng dụng web không cần chờ server để phản hồi
SQL Server 2025 — Cơ sở dữ liệu AI-ready với Vector Search, JSON gốc và RegEx
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.