如何为通用板条箱添加 WebAssembly 支持
本节适用于希望支持 WebAssembly 的通用板条箱作者。
您的板条箱可能已经支持 WebAssembly!
查看有关 哪些因素会导致通用板条箱不可移植到 WebAssembly 的信息。如果您的板条箱没有这些因素,它很可能已经支持 WebAssembly!
您可以始终通过为 WebAssembly 目标运行cargo build
来检查
cargo build --target wasm32-unknown-unknown
如果该命令失败,则您的板条箱目前不支持 WebAssembly。如果它没有失败,则您的板条箱可能支持 WebAssembly。您可以通过 添加 wasm 测试并在 CI 中运行这些测试 来 100% 确定它确实支持(并且继续支持)WebAssembly!
添加 WebAssembly 支持
避免直接执行 I/O
在 Web 上,I/O 始终是异步的,并且没有文件系统。将 I/O 从您的库中分离出来,让用户执行 I/O,然后将输入切片传递到您的库中。
例如,重构此代码
# #![allow(unused_variables)] #fn main() { use std::fs; use std::path::Path; pub fn parse_thing(path: &Path) -> Result<MyThing, MyError> { let contents = fs::read(path)?; // ... } #}
变成这样
# #![allow(unused_variables)] #fn main() { pub fn parse_thing(contents: &[u8]) -> Result<MyThing, MyError> { // ... } #}
添加wasm-bindgen
作为依赖项
如果您需要与外部世界交互(即您不能让库使用者为您驱动这种交互),那么您需要添加wasm-bindgen
(以及js-sys
和web-sys
,如果您需要它们)作为依赖项,用于编译目标为 WebAssembly 时
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = "0.3"
避免同步 I/O
如果您必须在您的库中执行 I/O,那么它不能是同步的。Web 上只有异步 I/O。使用 futures
板条箱 和 wasm-bindgen-futures
板条箱 来管理异步 I/O。如果您的库函数对某个未来类型F
是泛型的,那么该未来可以通过 Web 上的fetch
或操作系统提供的非阻塞 I/O 来实现。
# #![allow(unused_variables)] #fn main() { pub fn do_stuff<F>(future: F) -> impl Future<Item = MyOtherThing> where F: Future<Item = MyThing>, { // ... } #}
您还可以定义一个特征并为 WebAssembly 和 Web 以及本地目标实现它
# #![allow(unused_variables)] #fn main() { trait ReadMyThing { type F: Future<Item = MyThing>; fn read(&self) -> Self::F; } #[cfg(target_arch = "wasm32")] struct WebReadMyThing { // ... } #[cfg(target_arch = "wasm32")] impl ReadMyThing for WebReadMyThing { // ... } #[cfg(not(target_arch = "wasm32"))] struct NativeReadMyThing { // ... } #[cfg(not(target_arch = "wasm32"))] impl ReadMyThing for NativeReadMyThing { // ... } #}
避免生成线程
Wasm 还不支持线程(但 正在进行实验性工作),因此尝试在 wasm 中生成线程将导致恐慌。
您可以使用#[cfg(..)]
来根据目标是 WebAssembly 还是其他目标来启用线程和非线程代码路径
# #![allow(unused_variables)] #![cfg(target_arch = "wasm32")] #fn main() { fn do_work() { // Do work with only this thread... } #![cfg(not(target_arch = "wasm32"))] fn do_work() { use std::thread; // Spread work to helper threads.... thread::spawn(|| { // ... }); } #}
另一种选择是从您的库中分离出线程生成,并允许用户“自带线程”,类似于分离出文件 I/O 并允许用户自带 I/O。这具有与希望拥有自己的自定义线程池的应用程序配合使用的副作用。
维护对 WebAssembly 的持续支持
在 CI 中为wasm32-unknown-unknown
构建
通过让您的 CI 脚本运行以下命令来确保编译在针对 WebAssembly 时不会失败
rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown
例如,您可以将此添加到您的.travis.yml
配置中,用于 Travis CI
matrix:
include:
- language: rust
rust: stable
name: "check wasm32 support"
install: rustup target add wasm32-unknown-unknown
script: cargo check --target wasm32-unknown-unknown
在 Node.js 和无头浏览器中测试
您可以使用wasm-bindgen-test
和wasm-pack test
子命令在 Node.js 或无头浏览器中运行 wasm 测试。您甚至可以将这些测试集成到您的 CI 中。