前言
最近项目上有个功能, 需要前后端共用一套代码, 后端用的是Rust, 前端的开发工具链是Vite。 这里主要记录一下如何将Rust库打包成Wasm在前端使用。
Rust侧工作
完成Rust lib逻辑
首先是用Rust完成主要逻辑, 这个库我把它取名为quill-delta-parser。
quill是一个富文本编辑器, delta是它用的一种DSL,而这个库则是用于将Delta转换为Html。
新建打包项目
首先安装cargo-generate, 它可以通过一个模板创建rust项目(不安装也可以, 直接clone下面的git地址):
cargo install cargo-generate
接着使用cargo-generate创建一个用于打包的rust项目:
cargo generate --git https://github.com/rustwasm/wasm-pack-template
输入项目名称后回车即可。
进入项目文件夹后先修改Cargo.toml内的项目名等字段, 在依赖中添加上面写的lib:
[package]
name = "rust-wasm-test"
# ...
[dependencies]
wasm-bindgen = "0.2.63"
# ...
quill-delta-parser = { git = "https://github.com/FPG-Alan/quill-delta-parser.git", rev="dc21e20" }
可以看到依赖里有个wasm-bindgen, 它主要是用来提供 JS 和 Rust 之间交互的接口。
设置与JS交互的函数
下面就进入src/lib.rs内创建与JS交互的函数, 这里我只需要从quill-delta-parser中导出一个函数parse:
use quill_delta_parser::{parser, DeltaOp};
#[wasm_bindgen]
pub fn parse(delta: &JsValue) -> JsValue {
if let Ok(serde_delta) = delta.into_serde::<Vec<DeltaOp>>(){
return JsValue::from_str(&parser(serde_delta));
} else {
return JsValue::from_str("data parse error");
}
}
通过使用#[wasm_bindgen]注解, 这个parse函数后面在打包时就可以被JS调用了。
打包
打包使用rustwasm/wasm-pack, 安装后直接运行
wasm-pack build -t web
打包结果如下:
可以看到包含一个wasm文件, 一个类型声明文件, 还有一个wrapper js 文件, 如果想要发布到npm, 在这个直接执行目录npm publish即可。
这样, Rust侧的工作就完成啦。
Vite配置
vite 这边直接使用打包出来的模块的话还有两个问题:
- 依赖预构建时, esbuild会把import.meta.url编译成import_meta.url, 而import_meta是一个空对象(esbuild-repl), 导致wrapper js内执行初始化时无法得到正确的URL。
- 配置
optimizeDeps.esbuildOptions.target为es2020可以解决上面的问题, 但vite没有将该模块引用的wasm文件放入node_modules/.vite, 依然访问不到。
vite的issue列表里也找到了相关的一些issues:
bug(wasm): wasm cannot execute succeed in vite · Issue #8427 · vitejs/vite (github.com)
目前的解决方案是配置Vite在预构建时忽略这个wasm模块:
optimizeDeps: {
exclude: ['wasm-module-name'],
}
使用
配置完成后就可以像用一个普通node module那样引用了, 注意要先通过init函数初始化之后再调用导出的其他函数:
import init, { parse } from 'quill-delta-parser-wasm/quill_delta_parser_wasm';
init().then(() => { parse(deltaOps) });