Giao diện
Tích hợp WebAssembly
"Chạy Rust trong trình duyệt với tốc độ gần native."
1. Biên dịch Rust sang WASM
Pipeline biên dịch Rust sang WASM
Pipeline Rust sang WASM
┌──────────────────────────────────────────────────────────┐
│ │
│ Rust Source LLVM IR WASM Binary │
│ ─────────── ─────── ─────────── │
│ │
│ lib.rs ──▶ .ll ──▶ .wasm │
│ │
│ Cargo.toml pkg/ │
│ ├── [lib] ├── mylib.js │
│ └── crate-type = ["cdylib"] ├── mylib.wasm │
│ └── mylib.d.ts │
│ │
│ wasm-pack build ─────────────────────────────────────▶ │
│ │
└──────────────────────────────────────────────────────────┘Thiết lập Project
toml
# Cargo.toml
[package]
name = "my-wasm-lib"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
[profile.release]
lto = true
opt-level = "z" # Tối ưu cho kích thướcExport WASM cơ bản
rust
// src/lib.rs
use wasm_bindgen::prelude::*;
// Export hàm cho JavaScript
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Xin chào, {}!", name)
}Lệnh Build
bash
# Cài đặt target
rustup target add wasm32-unknown-unknown
# Cài đặt wasm-pack
cargo install wasm-pack
# Build cho web
wasm-pack build --target web
# Build cho bundler (webpack, rollup)
wasm-pack build --target bundler
# Build cho Node.js
wasm-pack build --target nodejs2. wasm-bindgen: Type Bindings
Giới hạn kiểu WASM
Các kiểu WASM Native
┌────────────────────────────────────────────────┐
│ │
│ Chỉ các kiểu số: │
│ • i32, i64 │
│ • f32, f64 │
│ │
│ KHÔNG hỗ trợ trực tiếp: │
│ • Strings │
│ • Structs │
│ • Arrays │
│ • Objects │
│ │
│ wasm-bindgen cầu nối khoảng cách này! │
│ │
└────────────────────────────────────────────────┘Cách wasm-bindgen hoạt động
Luồng chuyển đổi kiểu
┌─────────────────────────────────────────────────────────┐
│ │
│ JavaScript wasm-bindgen Rust │
│ ────────── ──────────── ──── │
│ │
│ "hello" ──▶ Encode UTF-8 ──▶ &str │
│ ──▶ Ghi vào WASM memory ──▶ │
│ ──▶ Truyền pointer + length ──▶ │
│ │
│ { a: 1 } ──▶ Serialize vào ──▶ MyStruct │
│ ──▶ linear memory ──▶ │
│ │
└─────────────────────────────────────────────────────────┘Làm việc với Strings
rust
use wasm_bindgen::prelude::*;
// &str: Mượn từ JS (zero-copy đọc)
#[wasm_bindgen]
pub fn count_chars(s: &str) -> usize {
s.chars().count()
}
// String: Owned, trả về JS
#[wasm_bindgen]
pub fn to_uppercase(s: &str) -> String {
s.to_uppercase()
}
// Cách nó hoạt động bên trong:
// 1. JS encode string thành UTF-8
// 2. Ghi bytes vào WASM linear memory
// 3. Truyền (ptr, len) cho Rust
// 4. Rust đọc từ memoryExport Structs
rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Point {
x: f64,
y: f64,
}
#[wasm_bindgen]
impl Point {
// Constructor phải đặt tên "new" cho JS class syntax
#[wasm_bindgen(constructor)]
pub fn new(x: f64, y: f64) -> Point {
Point { x, y }
}
// Getter
#[wasm_bindgen(getter)]
pub fn x(&self) -> f64 {
self.x
}
// Setter
#[wasm_bindgen(setter)]
pub fn set_x(&mut self, x: f64) {
self.x = x;
}
// Method
pub fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
}Sử dụng trong JavaScript
javascript
import init, { Point, add, greet } from './pkg/my_wasm_lib.js';
async function main() {
// Khởi tạo WASM module
await init();
// Sử dụng exported functions
console.log(add(2, 3)); // 5
console.log(greet("World")); // "Xin chào, World!"
// Sử dụng exported class
const p1 = new Point(0, 0);
const p2 = new Point(3, 4);
console.log(p1.distance(p2)); // 5
// Quan trọng: Giải phóng WASM memory khi xong
p1.free();
p2.free();
}
main();Import hàm JavaScript
rust
use wasm_bindgen::prelude::*;
// Import hàm JS
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_namespace = Math)]
fn random() -> f64;
}
// Import kiểu JS
#[wasm_bindgen]
extern "C" {
type HTMLDocument;
#[wasm_bindgen(static_method_of = HTMLDocument, js_class = "document")]
fn getElementById(id: &str) -> Option<Element>;
}
#[wasm_bindgen]
pub fn greet_alert(name: &str) {
log(&format!("Chào {}", name));
alert(&format!("Xin chào, {}!", name));
}JsValue: Giá trị JavaScript động
rust
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
// Nhận bất kỳ giá trị JS nào
#[wasm_bindgen]
pub fn process_value(val: JsValue) -> JsValue {
// Kiểm tra kiểu
if val.is_null() {
return JsValue::from_str("giá trị null");
}
if let Some(num) = val.as_f64() {
return JsValue::from_f64(num * 2.0);
}
if let Some(s) = val.as_string() {
return JsValue::from_str(&s.to_uppercase());
}
JsValue::UNDEFINED
}3. Tối ưu kích thước WASM Binary
Chiến lược tối ưu kích thước
toml
# Cargo.toml
[profile.release]
lto = true # Link-time optimization
opt-level = "z" # Tối ưu cho kích thước (vs "3" cho tốc độ)
codegen-units = 1 # Giảm parallelism để opt tốt hơn
panic = "abort" # Không có unwinding code
strip = true # Xóa symbolsPhân tích kích thước
bash
# Cài đặt công cụ phân tích kích thước
cargo install twiggy
# Phân tích cái gì chiếm không gian
twiggy top -n 20 target/wasm32-unknown-unknown/release/my_lib.wasm
# Hiển thị cây phụ thuộc
twiggy dominators my_lib.wasmwasm-opt Post-Processing
bash
# Cài đặt binaryen
# macOS: brew install binaryen
# Ubuntu: apt install binaryen
# Tối ưu WASM binary
wasm-opt -Oz -o optimized.wasm input.wasm
# Các Binaryen passes:
# -O0: Không tối ưu
# -O1: Tối ưu nhanh
# -O2: Tối ưu nhiều hơn
# -O3: Tối ưu nhiều nhất (tốc độ)
# -Os: Tối ưu cho kích thước
# -Oz: Tối ưu kích thước mạnh mẽSo sánh kích thước
So sánh kích thước Binary
┌─────────────────────────────────────────────────────────┐
│ │
│ Cấu hình Kích thước │
│ ──────── ────────── │
│ │
│ Debug build ~2 MB │
│ Release (mặc định) ~500 KB │
│ Release + LTO ~200 KB │
│ Release + LTO + opt-level=z ~100 KB │
│ + wasm-opt -Oz ~80 KB │
│ + wasm-gc (xóa dead code) ~50 KB │
│ │
│ Mẹo: Dùng feature flags để │
│ biên dịch chỉ những gì cần │
│ │
└─────────────────────────────────────────────────────────┘Giảm Dependencies
rust
// Dùng core/alloc thay vì std khi có thể
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use alloc::string::String;
// Hoặc tắt default features
// [dependencies]
// serde = { version = "1.0", default-features = false, features = ["alloc"] }4. Patterns nâng cao
Quản lý Memory
rust
use wasm_bindgen::prelude::*;
use std::alloc::{alloc, dealloc, Layout};
// Expose allocator cho JS quản lý memory
#[wasm_bindgen]
pub fn wasm_alloc(size: usize) -> *mut u8 {
let layout = Layout::from_size_align(size, 1).unwrap();
unsafe { alloc(layout) }
}
#[wasm_bindgen]
pub fn wasm_dealloc(ptr: *mut u8, size: usize) {
let layout = Layout::from_size_align(size, 1).unwrap();
unsafe { dealloc(ptr, layout) }
}Streaming Compilation
javascript
// Stream và compile WASM đồng thời
const response = await fetch('module.wasm');
const module = await WebAssembly.compileStreaming(response);
const instance = await WebAssembly.instantiate(module);Web Workers
javascript
// worker.js
import init, { process } from './pkg/my_lib.js';
self.onmessage = async (e) => {
await init();
const result = process(e.data);
self.postMessage(result);
};
// main.js
const worker = new Worker('worker.js', { type: 'module' });
worker.postMessage(data);
worker.onmessage = (e) => console.log(e.data);🎯 Best Practices
Khi nào dùng WASM
| Use Case | Lợi ích WASM |
|---|---|
| Xử lý ảnh | Nhanh hơn JS 10-20x |
| Mã hóa | Constant-time operations |
| Game physics | Hiệu năng dự đoán được |
| Nén dữ liệu | CPU-intensive |
| Encode video | Tính toán nặng |
Khi KHÔNG nên dùng WASM
- Thao tác DOM đơn giản (JS nhanh hơn cho glue code)
- Network requests (dùng Fetch API trực tiếp)
- Tính toán nhỏ (overhead gọi WASM)