从 "wasm 模块" 到 "wasm 组件"
传统 WebAssembly 的痛点在于接口:模块之间只能传递 i32/i64/f32/f64 四种数值,字符串、结构体都要靠手工管理线性内存和偏移量,跨语言调用极其痛苦。
组件模型(Component Model) 用 WIT(Wasm Interface Type) 解决这件事:用 IDL 描述接口,工具链自动生成绑定代码,让不同语言写的组件可以像函数调用一样互相调用。
第一个组件:用 Rust 编写
先定义接口:
// wit/calculator.wit
package example:calculator@0.1.0;
interface ops {
add(a: s32, b: s32) -> s32;
// 字符串、列表、record 都是一等类型
format(value: f64, unit: string) -> string;
stats(values: list<f64>) -> tuple<f64, f64>;
}
world calculator {
export ops;
}Rust 侧实现:
// src/lib.rs
wit_bindgen::generate!({
world: "calculator",
exports: { "example:calculator/ops": Ops },
});
struct Ops;
impl exports::example::calculator::ops::Guest for Ops {
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn format(value: f64, unit: String) -> String {
format!("{value:.2}{unit}")
}
fn stats(values: Vec<f64>) -> (f64, f64) {
let sum: f64 = values.iter().sum();
let avg = if values.is_empty() { 0.0 } else { sum / values.len() as f64 };
(sum, avg)
}
}# 安装组件工具链
cargo install cargo-component
# 编译为 wasm 组件
cargo component build --release
# 产物:target/wasm32-wasip2/release/calculator.wasm注意目标三元组是 wasm32-wasip2(WASI 0.2),而不是旧的 wasm32-wasi。
在宿主中调用(JavaScript)
npm i @bytecodealliance/jco// 生成 JS 绑定
npx jco transpile calculator.wasm -o distimport { calculator } from "./dist/calculator.js";
const { ops } = calculator;
console.log(ops.add(2, 3)); // 5
console.log(ops.format(12.3456, "ms")); // "12.35ms"
console.log(ops.stats([1, 2, 3, 4])); // [10, 2.5]字符串和列表自动完成编解码,宿主完全不需要碰线性内存。
WASI 0.2:标准化的系统接口
WASI Preview 2 把系统能力拆成一组接口,并引入异步支持:
use wasip2::*;
#[tokio::main(flavor = "current_thread")]
async fn main() {
// 标准输出
println!("start");
// 异步 HTTP 出站请求(wasi:http/outgoing-handler)
let req = http::types::OutgoingRequest::new(http::types::Headers::new());
req.set_method(&http::types::Method::Get).unwrap();
req.set_scheme(Some(&http::types::Scheme::Https)).unwrap();
req.set_authority(Some("api.github.com")).unwrap();
req.set_path_with_query(Some("/")).unwrap();
let fut = http::outgoing_handler::handle(req, None).unwrap();
let resp = futures::executor::block_on(fut).unwrap();
println!("status: {:?}", resp.status());
// 单调时钟
let now = clocks::monotonic_clock::now();
println!("uptime ns: {now}");
}这意味着 wasm 不再只能做纯计算,它可以安全地进行 IO、拥有文件系统沙箱、被授予精确的能力清单。
典型应用场景
- 插件系统:为主程序提供多语言插件能力,且有内存与系统调用沙箱(Figma、Shopify、Envoy 都在用)。
- 边缘计算:wasm 冷启动在毫秒级,镜像体积几百 KB,比容器更适合 Serverless。
- AI 推理与数据处理:把 Python/Rust 写的算子编译成组件,在任意宿主里复用。
生态现状与建议
# 运行时选型
wasmtime --version # Bytecode Alliance,组件模型支持最完整
wasmtime serve calculator.wasm- 组件模型规范已接近稳定,WASI 0.2 在 Wasmtime 中默认可用;
- 尚未进入浏览器原生支持(需 jco polyfill);
- 建议先从“内部插件”这类可控场景切入,而不是直接替换核心服务。
小结
组件模型补齐了 WebAssembly 最后一块拼图——跨语言的高层接口。如果说过去的 wasm 是“能在任何地方跑的 CPU”,那么现在的 wasm 组件就是“能在任何地方调用的软件模块”。
评论 0
还没有评论,来说两句吧。