如何為通用用途箱子添加 WebAssembly 支援
此章節適用於想要支援 WebAssembly 的通用用途箱子作者。
你的箱子可能已經支援 WebAssembly!
檢閱關於 那些可能讓通用用途箱子不適用於 WebAssembly 的事項。如果你的箱子裡沒有那些事項,它可能已經支援 WebAssembly!
你可以隨時執行 WebAssembly 目標的 cargo build
來確認
cargo build --target wasm32-unknown-unknown
如果指令執行失敗,就表示你的箱子現在不支援 WebAssembly。如果執行成功,你的箱子可能支援 WebAssembly。你可以透過新增 wasm 測試並在 CI 中執行測試來確切確認箱子支援 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。如果你的函式庫函式是泛型,且帶有一些 future 類型 `F`,則該 future 可以透過 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 CI 的 ` .travis.yml ` 組態中
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 中。