参考:《编译 Rust 为 WebAssembly》
https://developer.mozilla.org/zh-CN/docs/WebAssembly/Rust_to_Wasm
一、环境搭建
(一)安装工具链
1、wasm-bindgen
Facilitating high-level interactions between Wasm modules and JavaScript
https://github.com/rustwasm/wasm-bindgen
# 使用 Cargo 安装
cargo install wasm-bindgen-cli
文档:
Introduction - The wasm-bindgen
Guide
https://rustwasm.github.io/wasm-bindgen/introduction.html
https://rustwasm.github.io/
介绍- The wasm-bindgen
Guide
https://llever.com/wasm-bindgen/
2、wasm-pack
Your favorite rust -> wasm workflow tool!
https://github.com/rustwasm/wasm-pack
# 使用 Cargo 安装
cargo install wasm-pack
# 使用 Homebrew 安装
brew install wasm-pack
3、wasm-tools
(可选)
Low level tooling for WebAssembly in Rust
https://github.com/bytecodealliance/wasm-tools
# 使用 Cargo 安装
cargo install wasm-tools
# 使用 Homebrew 安装
brew install wasm-tools
4、binaryen
(包含 wasm-opt,可选)
Optimizer and compiler/toolchain library for WebAssembly
https://github.com/WebAssembly/binaryen
# 使用 Homebrew 安装
brew install binaryen
# 通过 Cargo 安装 Rust 版 wasm-opt
cargo install wasm-opt
(二)添加平台支持
增加对 WebAssembly 的支持:
# 查看当前 Rust 环境支持构建的平台
rustup target list
# 添加 WebAssembly 的支持
rustup target add wasm32-unknown-unknown
二、创建工程
(一)手工创建
1、创建 Rust 库工程
cargo new --lib hello-wasm
2、编写代码
清理掉 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));
}
- 通过
#[wasm_bindgen]
来指定与外部 JavaScript 进行交互。-
extern
用于通过 Rust 调用外部的 JavaScript 代码
-
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}
- 供外部 JavaScript 调用的 Rust 代码
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
3、配置 Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
(二)使用 wasm-pack
工具创建
创建工程
wasm-pack new
三、构建 WebAssembly
(一)手工构建
# 使用 Cargo 构建
cargo build --target wasm32-unknown-unknown --release
构建结果在这里:
<工程目录>/target/wasm32-unknown-unknown/release/<工程名>.wasm
(二)使用 wasm-pack
工具构建
构建工程
wasm-pack build -t web
构建结果在这里:
<工程名目录>/pkg/
四、踩坑
(一).wasm
文件与 JavaScript 的关系
从本质上说,JavaScript 可以通过 Ajax 将 .wasm
文件加载为二进制流,然后调用 WebAssembly
对象加载这个二进制流,从而完成对 .wasm
文件的加载,并完成初始化。这时已经可以通过 JavaScript 调用 .wasm
文件中的方法了。
如果要在 Rust 中支持这种方式,那么需要按如下方式编写函数:
// 使用 #[no_mangle] 防止名称修饰,使函数名可与其他语言的接口匹配
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
而且这种方式不需要什么三方依赖。通过 rustup target list
命令查看支持 wasm32-unknown-unknown
这个 target 就可以。
几种方式示例:https://github.com/Herbert8/simple-webassembly-demo/blob/main/web/utils.js
这种直接编写直接加载的方式,直观简单。缺点是对于复杂数据类型(如:字符串)的操作比较繁琐,需要自己在 JavaScript 中增加很多额外处理。这样的话,在组建复杂度比较高的场景,编写的 WebAssembly 难以作为成形的组件使用。
所以一般使用 Rust 编写的 WebAssembly 组件,都是结合着前端框架一起提供,便于使用。这也就是为什么 Rust 开发 WebAssembly 的教程都会涉及到 npm(这是很好的做法,但并不是必需的)。
(二)工具链在开发过程中的作用
根据前面的描述,在 WebAssembly 开发的过程中,除了业务代码,基础框架(包括 Rust 和 JavaScript)的工作多且繁琐,靠人工创建工程的方式效率太低,这个问题可以引入 wasm-pack
这个工具来解决。对于用于生产的系统,或者复杂的场景,不建议手工打造了,还是采用 wasm-pack
,能帮我们做以下事情:
1、创建工程
wasm-pack new my_proj
会根据一个 WebAssembly 的工程模板进行工程创建,包括基础代码和配置,省去手工配置的繁琐。
2、工程的构建
wasm-pack build -t web
# 或
wasm-pack build --target web
这里指定 target 非常重要!!!因为在 build 的时候,除了生成 WebAssembly,还会生成配套的 JavaScript 包装,以便于外部调用。当没有明确指定 target 时会使用 bundler
,这种方式不适合 Web,使用时报错。
3、wasm-pack
也会与前面提及的其他工具链集成,同时完成代码优化、瘦身以及包装文件的生成。
(三)包装文件的使用
使用 wasm-pack
构建后,除了 .wasm
文件,还会生成其对应的 JavaScript 包装文件。这里说下包装文件的使用规则。
1、包装文件使用了 ES6 模块机制
2、包装文件默认导出的是它的初始化器
3、需要在初始化完成后,再调用自己的方法
// 导入 js 中提供的函数
import init, { my_func1 as myFunc1, my_func2 as myFunc2 } from 'my_proj.js'
// 初始化(异步执行,通过 await 等待,必要时做好相关异常处理)
await init()
myFunc1()
myFunc2()
(四)开发工作流的优化
采用 VS Code 进行开发是不错的选择,能编写 Rust、配置文件以及前端代码。但每次修改后编译、将 .wasm
及包装文件部署到 Web 服务器,修改前端代码后也需要部署,还是有些繁琐。
这里推荐使用 make,自行编写 Makefile,VS Code 内置了 Makefile 的支持。可以根据自己的需要编写 Makefile,根据自己要做的事情定义 Target,配合 VS Code 自定义快捷键的功能,感觉整个开发工作流没那么繁琐了。
大家有什么好的建议请回复。
五、遗留问题
Debug 方面没找到好办法,还在研究中。有了新发现随时补充上来。
(完)
网友评论