中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript和Rust中wasm-bindgen組件有什么用

發布時間:2021-08-06 10:35:01 來源:億速云 閱讀:237 作者:小新 欄目:web開發

這篇文章主要介紹JavaScript和Rust中wasm-bindgen組件有什么用,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

WebAssembly標準只定義了四種類型:兩種整數類型和兩種浮點類型。然而,大多數情況下,JS和Rust開發人員正在使用更豐富的類型! 例如,JS開發人員經常與互以添加或修改HTML節點相關的文檔交互,而Rust開發人員使用類似Result等類型進行錯誤處理,幾乎所有程序員都使用字符串。

被局限在僅使用由WebAssembly所提供的類型將會受到太多的限制,這就是wasm-bindgen出現的原因。

wasm-bindgen的目標是提供一個JS和Rust類型之間的橋接。它允許JS使用字符串調用Rust API,或Rust函數捕獲JS異常。

wasm-bindgen抹平了WebAssembly和JavaScript之間的阻抗失配,確保JavaScript可以高效地調用WebAssembly函數,并且無需boilerplate,同時WebAssembly可以對JavaScript函數執行相同的操作。

wasm-bindgen項目在其README文件中有更多描述。要入門,讓我們深入到一個使用wasm-bindgen的例子中,然后探索它還有提供了什么。

1、Hello World!

學習新工具的最好也是最經典的方法之一就是探索下用它來輸出“Hello, World!”。在這里,我們將探索一個這樣的例子——在頁面里彈出“Hello World!”提醒框。

這里的目標很簡單,我們想要定義一個Rust的函數,給定一個名字,它會在頁面上創建一個對話框,上面寫著Hello,$name!在JavaScript中,我們可以將這個函數定義為:

代碼

export function greet(name) {
  alert(`Hello, ${name}!`);
}

不過在這個例子里要注意的是,我們將把它用Rust編寫。這里已經發生了很多我們必須要處理的事情:

  • JavaScript將會調用一個WebAssembly 模塊, 模塊名是 greetexport.

  • Rust函數將一個字符串作為輸入參數,也就是我們要打招呼的名字。

  • 在內部Rust會生成一個新的字符串,也就是傳入的名字。

  • 最后Rust會調用JavaScript的 alert函數,以剛創建的字符串作為參數。

啟動第一步,我們創建一個新的Rust工程:

代碼

$ cargo new wasm-greet --lib

這將初始化一個新的wasm-greet文件夾,我們的工作都在這里面完成。接下來我們要使用如下信息修改我們的Cargo.toml(在Rust里相當于package.json):

代碼

[lib] 
crate-type = ["cdylib"] 
 
[dependencies] 
wasm-bindgen = "0.2"

我們先忽略[lib]節的內容,接下來的部分聲明了對wasm-bindgen的依賴。這里的依賴包含了我們使用wasm-bindgen需要的所有的支持包。

接下來,是時候編寫一些代碼了!我們使用下列內容替換了自動創建的src/lib.rs:

代碼

#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
  fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
  alert(&format!("Hello, {}!", name));
}

如果你不熟悉Rust,這可能看起來有點啰嗦,但不要害怕!隨著時間的推移,wasm-bindgen項目不斷改進,而且可以肯定的是,所有這些并不總是必要的。

要注意的最重要的一點是#[wasm_bindgen]屬性,這是一個在Rust代碼中的注釋,這里的意思是“請在必要時用wrapper處理這個”。我們對alert函數的導入和greet函數的導出都被標注為這個屬性。稍后,我們將看到在引擎蓋下發生了什么。

首先,我們從在瀏覽器中打開作為例子來切入正題!我們先編譯wasm代碼:

代碼

$ rustup target add wasm32-unknown-unknown --toolchain nightly # only needed once 
$ cargo +nightly build --target wasm32-unknown-unknown

這段代碼會生成一個wasm文件,路徑為target/wasm32-unknown-unknown/debug/wasm_greet.wasm。如果我們使用工具如wasm2wat來看這個wasm文件里面的內容,可能會有點嚇人。

結果發現這個wasm文件實際上還不能直接被JS調用!為了能讓我們使用,我們需要執行一個或更多步驟:

代碼

$ cargo install wasm-bindgen-cli # only needed once 
$ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm --out-dir .

很多不可思議的事情發生都發生在這個步驟中:wasm-bindgen CLI工具對輸入的wasm文件做后期處理,使它變的“suitable”可用。

我們待會再來看“suitable”的意思,現在我們可以肯定的說,如果我們引入剛創建的wasm_greet.js文件(wasm-bindgen工具創建的),我們已經獲取到了在Rust中定義的greet函數。

最終我們接下來要做的是使用bundler對其打包,然后創建一個HTML頁面運行我們的代碼。

在寫這篇文章的時候,只有Webpack's 4.0 release對WebAssembly的使用有足夠的支持(盡管暫時已經有了 Chrome caveat)。

總有一天,更多的bundler也會接著支持WebAssmbly。在這我不再描述細節,但是你可以看一下在Github倉庫里的example配置。不過如果我們看內容,這個頁面中我們的JS在看起來是這樣的:
代碼

const rust = import("./wasm_greet"); 
rust.then(m => m.greet("World!"));

…就是這些了!現在打開我們的網頁就會顯示一個不錯的“Hello, World!”對話框,這就是Rust驅動的。

2、wasm-bindgen是如何工作的

唷,那是一個巨大的“Hello, World!”。讓我們深入了解一下更多的細節,以了解后臺發生了什么以及該工具是如何工作的。

wasm-bindgen最重要的方面之一就是它的集成基本上是建立在一個概念之上的,即一個wasm模塊僅是另一種ES模塊。例如,在上述中我們想要一個帶有如下簽名的ES模塊(在Typescript中):

代碼

export function greet(s: string);

WebAssembly無法在本地執行此操作(請記住,它目前只支持數字),所以我們依靠wasm-bindgen來填補空白。

在上述的最后一步中,當我們運行wasm-bindgen工具時,你會注意到wasm_greet.js文件與wasm_greet_bg.wasm文件一起出現。前者是我們想要的實際JS接口,執行任何必要的處理以調用Rust。* _bg.wasm文件包含實際的實現和我們所有的編譯后的代碼。

我們可以通過引入 ./wasm_greet 模塊得到 Rust 代碼愿意暴露出來的東西。我們已經看到了是如何集成的,可以繼續看看執行的結果如何。首先是我們的示例:

代碼

const rust = import("./wasm_greet"); 
rust.then(m => m.greet("World!"));

我們在這里以異步的方式導入接口,等待導入完成(下載和編譯 wasm)。然后調用模塊的 greet 函數。

注: 這里用到的異步加載目前需要 Webpack 來實現,但總會不需要的。而且,其它打包工具可能沒有此功能。

如果我們看看由 wasm-bindgen 工具為 wasm_greet.js 文件生成的內容,會看到像這樣的代碼:

代碼

import * as wasm from './wasm_greet_bg';

// ...

export function greet(arg0) {
  const [ptr0, len0] = passStringToWasm(arg0);
  try {
    const ret = wasm.greet(ptr0, len0);
    return ret;
  } finally {
    wasm.__wbindgen_free(ptr0, len0);
  }
}

export function __wbg_f_alert_alert_n(ptr0, len0) {
  // ...
}

注: 記住這是生成的,未經優化的代碼,它可能既不優雅也不簡潔!!在 Rust 中通過 LTO(Link Time Optimization,連接時優化)創建新的發行版,再通過 JS 打包工具流程(壓縮)之后,可能會精簡一些。

現在可以了解如何使用wasm-bindgen來生成greet函數。在底層它仍然調用wasm的greet函數,但是它是用一個指針和長度來調用的而不是用字符串。

了解passStringToWasm的更多細節可以訪問Lin Clark's previous post。它包含了所有的模板,對我們來說這是除了wasm-bindgen工具以外還需要去寫的東西!然后我們接下來看__wbg_f_alert_alert_n函數。

進入更深一層,下一個我們感興趣的就是WebAssmbly中的greet函數。為了了解這個,我們先來看Rust編譯器能訪問到的代碼。注意像上面生成的這種JS wrapper,在這里你不用寫greet的導出符號,#[wasm_bindgen]屬性會生成一個shim,由它來為你翻譯,命名如下:

代碼

pub fn greet(name: &str) {
  alert(&format!("Hello, {}!", name));
}

#[export_name = "greet"]
pub extern fn __wasm_bindgen_generated_greet(arg0_ptr: *mut u8, arg0_len: usize) {
  let arg0 = unsafe { ::std::slice::from_raw_parts(arg0_ptr as *const u8, arg0_len) }
  let arg0 = unsafe { ::std::str::from_utf8_unchecked(arg0) };
  greet(arg0);
}

現在可以看到原始代碼,greet,也就是由#[wasm_bindgen]屬性插入的看起來有意思的函數__wasm_bindgen_generated_greet。這是一個導出函數(用#[export_name]和extern關鍵詞來指定的),參數為JS傳進來的指針/長度對。在函數中它會將這個指針/長度轉換為一個&str (Rust中的一個字符串),然后將它傳遞給我們定義的greet函數。

從另一個方面看,#[wasm_bindgen]屬性生成了兩個wrappers:一個是在JavaScript中將JS類型的轉換為wasm,另外一個是在Rust中接收wasm類型并將其轉為Rust類型。

現在我們來看wrappers的最后一塊,即alert函數。Rust中的greet函數使用標準format!宏來創建一個新的字符串然后傳給alert。回想當我們聲明alert方法的時候,我們是使用 #[wasm_bindgen]聲明的,現在我們看看在這個函數中暴露給rustc的內容:

代碼

fn alert(s: &str) {
  #[wasm_import_module = "__wbindgen_placeholder__"]
  extern {
    fn __wbg_f_alert_alert_n(s_ptr: *const u8, s_len: usize);
  }
  unsafe {
    let s_ptr = s.as_ptr();
    let s_len = s.len();
    __wbg_f_alert_alert_n(s_ptr, s_len);
  }
}

這并不是我們寫的,但是我們可以看看它是怎么變成這樣的。alert函數事實上是一個簡化的wrapper,它帶有Rust的 &str然后將它轉換為wasm類型(數字)。它調用了我們在上面看到過的比較有意思的函數__wbg_f_alert_alert_n,然而它奇怪的一點就是#[wasm_import_module]屬性。

在WebAssembly中所有導入的函數都有一個其存在的模塊,而且由于wasm-bindgen構建在ES模塊之上,所以這也將被轉譯為ES模塊導入!

目前__wbindgen_placeholder__模塊實際上并不存在,但它表示該導入將被wasm-bindgen工具重寫,以從我們生成的JS文件中導入。

最后,對于最后一部分的疑惑,我們得到了我們所生成的JS文件,其中包含:

代碼

export function __wbg_f_alert_alert_n(ptr0, len0) {
  let arg0 = getStringFromWasm(ptr0, len0);
  alert(arg0)
}

哇! 事實證明,這里隱藏著相當多的東西,我們從JS中的瀏覽器中的警告都有一個相對較長的知識鏈。不過,不要害怕,wasm-bindgen的核心是所有這些基礎設施都被隱藏了! 你只需要在隨便使用幾個#[wasm_bindgen]編寫Rust代碼即可。然后你的JS可以像使用另一個JS包或模塊一樣使用Rust了。

wasm-bindgen還能做什么

wasm-bindgen項目在這個領域內志向遠大,我們在此不再詳細贅述。探索wasm-bindgen中的功能一個有效的方法就是探索示例目錄,這些示例涵蓋了從我們之前看到的Hello World! 到在Rust中對DOM節點的完全操作。

wasm-bindgen高級特性如下:

  • 引入JS結構,函數,對象等來在wasm中調用。你可以在一個結構中調用JS方法,也可以訪問屬性,這給人一種Rust是“原生”的感覺,讓人覺得你曾經寫過的Rust #[wasm_bindgen] annotations都可以連接了起來。

  • 將Rust結構和函數導出到JS。與只用JS使用數字類型來工作相比,你可以導出一個Rust結構并在JS中轉換成一個類。然后可以將結構傳遞,而不是只使用整形數值來傳遞。 smorgasboard 這個例子可以讓你體會支持的互操作特性。

  • 其他各種各樣的特性例如從全局范圍內導入(就像alert函數),在Rust中使用一個Result來獲取JS異常,以及在Rust程序中通用方法模擬存儲JS值。

如果你想了解更多的功能,繼續閱讀 issue tracker。

3、wasm-bindgen接下來做什么?

在我們結束之前,我想花一點時間來下描述wasm-bindgen的未來愿景,因為我認為這是當今項目最激動人心的一方面。

不僅僅支持Rust

從第1天起,wasm-bindgen CLI工具就設計成了多語言支持的。盡管Rust目前是唯一被支持的語言,但該工具也可以嵌入C或C++。 #[wasm_bindgen]屬性創建了可被wasm-bindgen工具解析并隨后刪除的輸出(* .wasm)文件的自定義部分。

本節介紹要生成哪些JS綁定以及它們的接口是什么。這個描述中沒有關于Rust的特定部分,因此C ++編譯器插件可以很容易地創建該部分,并通過wasm-bindgen工具進行處理。

我覺得這個方面特別令人振奮,因為我相信它使像wasm-bindgen這樣的工具成為WebAssembly和JS集成的標準做法。希望所有編譯為WebAssembly的語言都能受益,并且可以被bundler自動識別,以避免上述幾乎所有的配置和構建工具。

自動綁定JS生態

使用#[wasm_bindgen] 宏導入功能唯一不好的一面就是你必須將所有東西都寫出來,還要保證沒有任何錯誤。這種讓人覺得很單調(而且易錯)的操作的自動化技術已經成熟了。

所有的web APIs都由WebIDL指定,而且在generate #[wasm_bindgen] annotations from WebIDL是可行的。這個就意味著你不需要像前面一樣定義alert函數,而是你只需要寫下面這些:

代碼

#[wasm_bindgen]
pub fn greet(s: &str) {
  webapi::alert(&format!("Hello, {}!", s));
}

在這個例子中,WebIDL對web APIs的描述可以完全自動生成webapi集合,保證沒有錯誤。

我們甚至可以將自動化更進一步,TypeScript組織已經做了這方面的復雜工作,參照generate #[wasm_bindgen] from TypeScript as well。可以免費用npm上的TypeScript自動綁定任何包!

比 JS DOM 操作更快的性能

最后要說的事情對 wasm-bindgen 來說也很重要:超快的 DOM 操作 —— 這是很多 JS 框架的終極目標。如今需要使用一些中間工具來調用 DOM 函數,這些工具正在由 JavaScript 實現轉向 C++ 引擎實現。然而,在 WebAssembly 來臨之后,這些工具并非必須。WebAssembly 是有類型的。

從第一天起,wasm-bindgen 代碼生成的設計就考慮到了將來的宿主綁定方案。當這一特征出現在 WebAssembly 之后,我們可以直接調用導入的函數,而不需要 wasm-bindgen 的中間工具。

此外,它使得 JS 引擎積極優化 WebAssembly 對 DOM 的操作,使其對類型的支持更好,而且在調用 JS 的時候不再需要進行參數驗證。在這一點上,wasm-bindgen 不僅在操作像 string 這樣的富類型變得容易,還提供了一流的 DOM 操作性能。

以上是“JavaScript和Rust中wasm-bindgen組件有什么用”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新兴县| 平定县| 惠水县| 蚌埠市| 宁都县| 张家界市| 资源县| 青神县| 得荣县| 化州市| 班戈县| 安福县| 泰和县| 当雄县| 黄浦区| 东丽区| 彭泽县| 达日县| 南召县| 玉溪市| 海门市| 昌邑市| 梨树县| 松桃| 定边县| 德惠市| 乌鲁木齐市| 绥化市| 奇台县| 霍山县| 扶余县| 崇左市| 宽城| 伽师县| 儋州市| 阜康市| 苏尼特左旗| 田林县| 苍梧县| 水富县| 沁阳市|