Rust + WebAssembly 新手完全入门指南
这篇文章面向前端、Rust 开发者,只要跟着步骤就能跑通你的第一个 WebAssembly 前端组件。
WebAssembly 是什么
WebAssembly(简称 Wasm)是一种可在现代浏览器中运行的低级、紧凑、高效的二进制指令格式。它的出现主要是用来解决 JavaScript 天生的性能瓶颈,尤其是计算密集型场景下,WebAssembly 能达到接近原生的执行性能。
可以说,WebAssembly 的出现,使得在网页上运行高级、复杂的客户端应用(如图像编辑、Web AI 推理、复杂的游戏引擎)成为可能。
通过 caniuse,我们可以看到 WebAssembly 在主流浏览器中已经广泛可用,如下图所示:

环境准备
首先,确保你已经安装好 Nodejs 和 Rust 环境。这里我们会用到 wasm-pack,wasm-pack 是 Rust Wasm 开发的核心工具,负责构建、打包、测试 Wasm 项目。
# 安装 wasm-pack
cargo install wasm-pack
接下来,我们使用 wasm-pack 初始化项目:
# 初始化项目
wasm-pack new wasm-example
cd wasm-example
wasm-pack 会帮我们生成如下的目录结构:
.
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
├── src
│ ├── lib.rs
│ └── utils.rs
└── tests
└── web.rs
wasm-pack 初始化项目时默认使用的是 wasm-pack-template 这个模板,由于缺少维护,所以默认使用的 Edition、wasm-bindgen 等的版本都比较低,你可以手动调整下,但是在这个案例中,我们就暂时不动它们了。
在以后的开发中,你可以维护一个自己的模板,然后通过如下命令使用模板:
wasm-pack new <name> --template <template>
实现一个简单 Wasm 组件
我们首先实现一个简单 Wasm 组件,我们将会用到 js-sys 这个库,它包含 JavaScript 语言本身的 API,如数组、字符串、console、Promise 等。
# 添加 js-sys
cargo add js-sys
现在我们直接修改 src/lib.rs 文件:
# 默认模板里有 src/utils.rs,这里引入了它
mod utils;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn sum_array(arr: &js_sys::Array) -> u32 {
let mut sum = 0;
for i in 0..arr.length() {
let val = arr.get(i).as_f64().unwrap_or(0.0) as u32;
sum += val;
}
sum
}
#[wasm_bindgen] 属性宏声明了将导出 sum_array 函数以供 JS 调用,这就是我们这个简单组件提供的能力。接下来,我们进行编译:
wasm-pack build --target web
编译完成后,我们可以在 pkg 目录中看到编译结果:
pkg
├── package.json
├── README.md
├── wasm_example_bg.js
├── wasm_example_bg.wasm
├── wasm_example_bg.wasm.d.ts
├── wasm_example.d.ts
└── wasm_example.js
wasm_example_bg.wasm是核心的 Wasm 二进制文件,包含我们编写的 Rust 逻辑;wasm_example.js是 JS 胶水代码,负责 Wasm 的加载、类型转换,把 Rust 函数封装成 JS 可直接调用的方法;wasm_example.d.ts是 TypeScript 类型定义,用于支持 TS 项目。
这里的 --target web 表示我们的目标编译平台是 web,这样方便在 HTML 文件中引入。在根目录创建 index.html 并编辑:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rust + WebAssembly</title>
</head>
<body>
<script type="module">
import init, { sum_array } from './pkg/wasm_example.js';
await init();
console.log("求和结果为:", sum_array([10, 20, 30]));
</script>
</body>
</html>
由于浏览器的安全策略不允许加载本地 Wasm 模块,所以无法使用 file:// 协议打开 HTML 文件,我们需要一个本地 HTTP 服务器来启动项目。
npx serve .
这时候,打开浏览器的控制台就能够看到打印出来的求和结果了。

使用 Rust 操作 DOM
借助 web-sys,我们可以直接用 Rust 操作 DOM。与 js-sys 不同,web-sys 包含浏览器环境提供的 Web API。
cargo add web-sys --features "Window,Document,HtmlElement"
这里需要注意的是,web-sys 必须指定启用的 feature,不然编译不通过。
现在我们通过 web-sys 来修改网页标题,编辑 src/lib.rs,添加:
#[wasm_bindgen]
pub fn set_title(title: &str) {
// 获取 window 对象
let win = match web_sys::window() {
Some(w) => w,
None => return,
};
// 获取 document 对象
let doc = match win.document() {
Some(d) => d,
None => return,
};
// 修改网页标题
doc.set_title(title);
}
重新编译完成后,我们修改 index.html,添加:
<script type="module">
import init, { set_title, sum_array } from './pkg/wasm_example.js';
await init();
console.log("求和结果为:", sum_array([10, 20, 30]));
set_title("Hello world");
</script>
刷新网页后,我们就能够看到网页的标题被修改为 Hello world 了。
接入 Vite
在实际开发中,我们很少直接写原生 HTML,基本上是基于 Vite、Webpack 等构建工具进行开发,这里以最常用的 Vite 为例,演示如何集成 Rust Wasm。
npm create vite@latest wasm-vite-example -- --template vanilla-ts
cd wasm-vite-example
把之前的 wasm-example 项目整个拷贝到 wasm-vite-example 的根目录。在实际开发中,这种时候肯定是要用 monorepo 的,但在这个案例中就不这么弄了,一切从简。
.
├── .gitignore
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json
└── wasm-example
安装插件:
npm install -D vite-plugin-wasm-pack
创建 vite.config.ts 并编辑:
import { defineConfig } from 'vite';
import wasmPack from 'vite-plugin-wasm-pack';
export default defineConfig({
plugins: [wasmPack('./wasm-example')]
});
接下来,在 src/main.ts 中添加上我们之前写的业务逻辑,如下:
import init, { set_title, sum_array } from 'wasm_example'
async function initWasm() {
await init();
console.log("求和结果为:", sum_array([10, 20, 30]));
set_title("Hello world");
}
initWasm();
启动开发服务:
npm run dev
打开浏览器就能看到 Wasm 函数的执行结果了。
结尾
至此,我们就成功实现了第一个 WebAssembly 前端组件,虽然它很简陋,没有涉及到:
- 测试与调试,如:用
wasm-pack test进行单元测试、使用浏览器 DevTools 调试 Wasm 代码; - Wasm 高级特性,如共享内存、多线程、SIMD 指令等;
- 性能优化与踩坑。
不过没有关系,这些内容在后续的文章更新中可能会涉及到。现在,我们需要做的是动手实现一遍这个简单的 Wasm 组件。毕竟对于新手而言,最好的学习方式就是动手实践。