Rust+WASM+Vite使用记录

3,356 阅读2分钟

前言

最近项目上有个功能, 需要前后端共用一套代码, 后端用的是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

打包结果如下:

image.png

可以看到包含一个wasm文件, 一个类型声明文件, 还有一个wrapper js 文件, 如果想要发布到npm, 在这个直接执行目录npm publish即可。

这样, Rust侧的工作就完成啦。

Vite配置

vite 这边直接使用打包出来的模块的话还有两个问题:

  1. 依赖预构建时, esbuild会把import.meta.url编译成import_meta.url, 而import_meta是一个空对象(esbuild-repl), 导致wrapper js内执行初始化时无法得到正确的URL。
  2. 配置optimizeDeps.esbuildOptions.targetes2020可以解决上面的问题, 但vite没有将该模块引用的wasm文件放入node_modules/.vite, 依然访问不到。

vite的issue列表里也找到了相关的一些issues:

bug(wasm): wasm cannot execute succeed in vite · Issue #8427 · vitejs/vite (github.com)

import.meta.url not available in dependencies, wasm-pack modules unusable in dev mode? · Issue #7287 · vitejs/vite (githu…

目前的解决方案是配置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) });