本文假定读者已经熟悉 Rust 工具链,涉及的流程不会额外安装 npm 之类的工具链。
参考的文档是 wasm-bindgen 指南。
首先需要安装:
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
现在切到一个文件夹下新建一个项目:
cargo new --lib example
cd example
cargo add wasm-bindgen
此时 Cargo.toml 中的内容大概形如:
[package]
name = "example"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
我们在 ./src/lib.rs 中填入:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
现在可以运行:
cargo build --target wasm32-unknown-unknown --release
wasm-bindgen --out-dir ./dist --target web ./target/wasm32-unknown-unknown/release/example.wasm
这将在 ./dist 下生成若干个文件。
现在添加一个 index.html,内容为:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script type="module">
import init, { greet } from './dist/example.js';
async function run() {
await init();
greet('WebAssembly');
}
run();
</script>
</body>
</html>
由于 CORS 限制,在本地测试时直接用浏览器打开 index.html 无法成功,需要启动一个 HTTP 服务器。
这里可以使用 miniserve 轻量级服务器:
cargo install miniserve
miniserve . --index "index.html" -p 8080
现在就可由浏览器打开 http://localhost:8080 看到正确效果。
现在看一个更完整一点的例子(读者自行修改 index.html 对应的调用):
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
fn run() {
bare_bones();
using_a_macro();
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}
fn bare_bones() {
log("Hello from Rust!");
log_u32(42);
log_many("Logging", "many values!");
}
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
fn using_a_macro() {
console_log!("Hello {}!", "world");
console_log!("Let's print some numbers...");
console_log!("1 + 3 = {}", 1 + 3);
}
实际上 web-sys crate 定义好了 web_sys::console::log_1 这样的函数,如果读者认为这是好的选择可以尝试。
另参考导入非浏览器 JS 一节。
注意在发布时使用发布模式 cargo release 编译. 如果希望获得最小的二进制文件,可启用 LTO.
[profile.release]
lto = true