我初识 WebAssembly 是当初想要分析某个网站的加密算法,最终定位到了一个 .wasm
文件,没错,这个就是 WebAssembly 的构建产物,能够直接运行在浏览器中。在我当时看来这门技术很先进,不过如今看来绝大多数的 web 应用貌似都没使用上,迄今为止我也只在这个网站中看到使用 WebAssembly 的(也许有很多,只是没实质分析过)。
恰好最近正在接触 Rust,而 Rust 开发 WebAssembly 也非常方便,因此本文算是我对 Rust + WebAssembly 的初探。
有关 WebAssembly 不做过多介绍,你可以到 MDN 中查看相关介绍。本文重点于 Rust + WebAssembly 实践与相关工具,在 Rust and WebAssembly (github.com) 或 https://github.com/rwasm 中查看 rustwasm 相关生态。
使用 wasm-pack 打包 rust 为 wasm 文件
下载 wasm-pack,用于将 rust 代码打包成 .wasm 文件
cargo install wasm-pack
使用 cargo 有可能无法安装 wasm-pack(笔者就安装不了 openssl-sys),可以到 wasm-pack 官网下载对应的二进制文件进行安装。
构建 rust lib
cargo new --lib hello-wasm
将会创建 rust 库工程,并创建 src/lib.rs
。修改为以下内容(先不必在意代码)
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
接着在 Cargo.toml 文件中添加 wasm-bindgen 依赖,wasm-bindgen
来提供 JavaScript 和 Rust 类型之间的桥梁,允许 JavaScript 使用字符串调用 Rust API,或调用 Rust 函数来捕获 JavaScript 异常。
[package]
name = "hello-wasm"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
打包
wasm-pack build
WebAssembly 构建产物将会输出在 pkg 目录下,如下
├─pkg
| ├─.gitignore
| ├─hello_wasm.d.ts
| ├─hello_wasm.js
| ├─hello_wasm_bg.js
| ├─hello_wasm_bg.wasm
| └─hello_wasm_bg.wasm.d.ts
如果想当 npm 包发布的话,可以添加 —scope 参数,将会在 pkg 下生成 package.json 文件用于发布或当做一个 npm 包来使用,这样也可以在前端工程中直接当做一个模块来导入使用。
wasm-pack build --scope mynpmusername
借助 wasm-pack 可以非常轻松的将 rust 打包成 wasm,同时还提供了 js 相关支持。直接打包成 js 可导入的 npm 包,而不是让用户导入 wasm 文件然后通过浏览器 WebAssembly
对象来加载 WebAssembly 代码,其他语言的 WebAssembly 开发也是如此。
此外 rustwasm 还提供了对应的模板 rustwasm/wasm-pack-template,可以帮你省去上面的一系列配置操作,专注于你的 wasm 开发。
运行
由于上面我们已经将其打包成了一个 npm 包,只需要将配置好 package.json 的依赖即可,本地的话可通过下方格式,将 pkg 目录更改为 hello-wasm,并放置在根目录下。
"dependencies": {
"hello-wasm": "file:./hello-wasm"
},
这时候就可以通过 js 直接导入使用
const js = import("./hello-wasm/hello_wasm.js");
js.then(js => {
js.greet("WebAssembly");
});
在 vite 生态中有个 rwasm/vite-plugin-rsw 插件,能够在 vite 中快速使用 wasm-pack。下文中的一个应用示例也将采用该插件进行开发。
Rust 实现 MD5 算法
回到一开始的标题,在实现这个功能我一般会想 js 如何实现 MD5 算法,通常来说 MD5 算法是个比较流行的加密算法,通过搜索引擎能够快速帮我找到一份 js 的 MD5 算法。不过我更习惯通过包管理器导入的加密库,如crypto-js。
同理,在 rust 中可以到 crates.io 中也可以找到你想要的库,如 digest,不过我这里主要是实现 MD5 算法便使用的是 md-5。以下是我的封装代码。
use md5::{Digest, Md5};
fn md5(input: &str) -> String {
let mut hasher = Md5::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
format!("{:x}", result)
}
fn main() {
let result = md5("123456");
println!("{}", result);
}
然后将这一部分的代码替换到一开始的示例中。
extern crate wasm_bindgen;
extern crate md5;
use wasm_bindgen::prelude::*;
use md5::{Digest, Md5};
#[wasm_bindgen]
pub fn md5(input: &str)-> String {
let mut hasher = Md5::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
format!("{:x}", result)
}
此时通过 wasm-pack 将上述代码打包成 npm 包形式即可在 js 中调用 rust 提供的 md5 函数,至此就已经完成了本标题的内容了。
在项目中使用
这里我所借用 rwasm/vite-plugin-rsw 插件,在 vite 中配合 wasm-pack 进行开发的一个实例。代码部分就不做解读了,有兴趣可自行到翻阅源码:kuizuo/rust-wasm-md5