最近我們已經(jīng)見(jiàn)識(shí)了WebAssembly如何快速編譯、加速JS庫(kù)以及生成更小的二進(jìn)制格式。我們甚至為Rust和JavaScript社區(qū)以及其他Web編程語(yǔ)言之間的更好的互操作性制定了高級(jí)規(guī)劃。正如前面一篇文章中提到的,我想深入了解一個(gè)特定組件的細(xì)節(jié),wasm-bindgen。
今天WebAssembly標(biāo)準(zhǔn)只定義了四種類型:兩種整數(shù)類型和兩種浮點(diǎn)類型。然而,大多數(shù)情況下,JS和Rust開發(fā)人員正在使用更豐富的類型! 例如,JS開發(fā)人員經(jīng)常與互以添加或修改HTML節(jié)點(diǎn)相關(guān)的文檔交互,而Rust開發(fā)人員使用類似Result等類型進(jìn)行錯(cuò)誤處理,幾乎所有程序員都使用字符串。
被局限在僅使用由WebAssembly所提供的類型將會(huì)受到太多的限制,這就是wasm-bindgen出現(xiàn)的原因。
wasm-bindgen的目標(biāo)是提供一個(gè)JS和Rust類型之間的橋接。它允許JS使用字符串調(diào)用Rust API,或Rust函數(shù)捕獲JS異常。
wasm-bindgen抹平了WebAssembly和JavaScript之間的阻抗失配,確保JavaScript可以高效地調(diào)用WebAssembly函數(shù),并且無(wú)需boilerplate,同時(shí)WebAssembly可以對(duì)JavaScript函數(shù)執(zhí)行相同的操作。
wasm-bindgen項(xiàng)目在其README文件中有更多描述。要入門,讓我們深入到一個(gè)使用wasm-bindgen的例子中,然后探索它還有提供了什么。
1、Hello World!
學(xué)習(xí)新工具的最好也是最經(jīng)典的方法之一就是探索下用它來(lái)輸出“Hello, World!”。在這里,我們將探索一個(gè)這樣的例子——在頁(yè)面里彈出“Hello World!”提醒框。
這里的目標(biāo)很簡(jiǎn)單,我們想要定義一個(gè)Rust的函數(shù),給定一個(gè)名字,它會(huì)在頁(yè)面上創(chuàng)建一個(gè)對(duì)話框,上面寫著Hello,$name!在JavaScript中,我們可以將這個(gè)函數(shù)定義為:
代碼
export function greet(name) {undefined
alert(Hello, ${name}!);
}
不過(guò)在這個(gè)例子里要注意的是,我們將把它用Rust編寫。這里已經(jīng)發(fā)生了很多我們必須要處理的事情:
JavaScript將會(huì)調(diào)用一個(gè)WebAssembly 模塊, 模塊名是 greetexport.
Rust函數(shù)將一個(gè)字符串作為輸入?yún)?shù),也就是我們要打招呼的名字。
在內(nèi)部Rust會(huì)生成一個(gè)新的字符串,也就是傳入的名字。
最后Rust會(huì)調(diào)用JavaScript的 alert函數(shù),以剛創(chuàng)建的字符串作為參數(shù)。
啟動(dòng)第一步,我們創(chuàng)建一個(gè)新的Rust工程:
代碼
$ cargo new wasm-greet --lib
這將初始化一個(gè)新的wasm-greet文件夾,我們的工作都在這里面完成。接下來(lái)我們要使用如下信息修改我們的Cargo.toml(在Rust里相當(dāng)于package.json):
代碼
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
我們先忽略[lib]節(jié)的內(nèi)容,接下來(lái)的部分聲明了對(duì)wasm-bindgen的依賴。這里的依賴包含了我們使用wasm-bindgen需要的所有的支持包。
接下來(lái),是時(shí)候編寫一些代碼了!我們使用下列內(nèi)容替換了自動(dòng)創(chuàng)建的src/lib.rs:
代碼
![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
[wasm_bindgen]
extern {undefined
fn alert(s: &str);
}
[wasm_bindgen]
pub fn greet(name: &str) {undefined
alert(&format!("Hello, {}!", name));
}
如果你不熟悉Rust,這可能看起來(lái)有點(diǎn)啰嗦,但不要害怕!隨著時(shí)間的推移,wasm-bindgen項(xiàng)目不斷改進(jìn),而且可以肯定的是,所有這些并不總是必要的。
要注意的最重要的一點(diǎn)是#[wasm_bindgen]屬性,這是一個(gè)在Rust代碼中的注釋,這里的意思是“請(qǐng)?jiān)诒匾獣r(shí)用wrapper處理這個(gè)”。我們對(duì)alert函數(shù)的導(dǎo)入和greet函數(shù)的導(dǎo)出都被標(biāo)注為這個(gè)屬性。稍后,我們將看到在引擎蓋下發(fā)生了什么。
首先,我們從在瀏覽器中打開作為例子來(lái)切入正題!我們先編譯wasm代碼:
代碼
$ rustup target add wasm32-unknown-unknown --toolchain nightly # only needed once
$ cargo +nightly build --target wasm32-unknown-unknown
這段代碼會(huì)生成一個(gè)wasm文件,路徑為target/wasm32-unknown-unknown/debug/wasm_greet.wasm。如果我們使用工具如wasm2wat來(lái)看這個(gè)wasm文件里面的內(nèi)容,可能會(huì)有點(diǎn)嚇人。
結(jié)果發(fā)現(xiàn)這個(gè)wasm文件實(shí)際上還不能直接被JS調(diào)用!為了能讓我們使用,我們需要執(zhí)行一個(gè)或更多步驟:
代碼
$ cargo install wasm-bindgen-cli # only needed once
$ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm --out-dir .
很多不可思議的事情發(fā)生都發(fā)生在這個(gè)步驟中:wasm-bindgen CLI工具對(duì)輸入的wasm文件做后期處理,使它變的“suitable”可用。
我們待會(huì)再來(lái)看“suitable”的意思,現(xiàn)在我們可以肯定的說(shuō),如果我們引入剛創(chuàng)建的wasm_greet.js文件(wasm-bindgen工具創(chuàng)建的),我們已經(jīng)獲取到了在Rust中定義的greet函數(shù)。
最終我們接下來(lái)要做的是使用bundler對(duì)其打包,然后創(chuàng)建一個(gè)HTML頁(yè)面運(yùn)行我們的代碼。