亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

改進 JavaScript 和 Rust 的互操作性并深入認識 wasm-bindgen 組件

2024-05-06 16:53:20
字體:
來源:轉載
供稿:網友

前言

最近我們已經見識了WebAssembly如何快速編譯、加速JS庫以及生成更小的二進制格式。我們甚至為Rust和JavaScript社區以及其他Web編程語言之間的更好的互操作性制定了高級規劃。正如前面一篇文章中提到的,我想深入了解一個特定組件的細節,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?;叵氘斘覀兟暶鱝lert方法的時候,我們是使用 #[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??梢悦赓M用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 操作性能。

收工

我自己發現使用WebAssembly是異常令人振奮的,不僅僅是因為其社區,還因為其如此快速地在進度上突飛猛進。wasm-bindgen工具擁有光明的未來。它使JS和諸如Rust這樣的編程語言之間的互操作性變成了一流的體驗,并且隨著WebAssembly的不斷發展它也將提供了長期的好處。

試著給wasm-bindgen一次機會,因功能需求而創建一個問題,亦或繼續保持參與Rust和WebAssembly!

關于Alex Crichton(作者) 

Alex是Rust核心團隊的成員之一,自2012年底以來一直從事于Rust。目前他正在幫助WebAssembly Rust Working Group使得Rust + Wasm成為最佳體驗。Alex還幫助維護Cargo(Rust的包管理器),Rust標準庫以及Rust的發布和CI的基礎架構。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日本免费久久高清视频| 欧美国产视频日韩| 国产91精品久| 亚洲国产欧美在线成人app| 日韩中文视频免费在线观看| 国产婷婷成人久久av免费高清| 亚洲欧洲第一视频| 午夜精品一区二区三区在线视| 精品免费在线观看| 91在线观看欧美日韩| 日韩av免费看网站| 97视频在线观看免费高清完整版在线观看| 亚洲综合一区二区不卡| 91午夜在线播放| 91在线国产电影| 国产高清在线不卡| 国产成人精品日本亚洲专区61| 国产丝袜高跟一区| 亚洲日韩欧美视频一区| 69av在线播放| 亚洲一品av免费观看| 日韩中文字幕在线免费观看| 91精品国产高清自在线看超| 久久精品视频在线播放| 国产精品稀缺呦系列在线| 亚洲图片欧美日产| 日韩精品中文字幕在线观看| 国产欧美日韩精品在线观看| 亚洲国产成人爱av在线播放| 久久久精品欧美| 欧美日韩国产一区在线| 国产成人短视频| 久久久久久12| 日韩精品黄色网| 欧美三级免费观看| 国产精品色午夜在线观看| 欧美午夜激情在线| 国产一区二区丝袜| 国产精品久久网| 欧美精品一二区| 在线播放国产精品| 成人网中文字幕| 欧美精品久久久久久久免费观看| 日韩电影中文 亚洲精品乱码| 精品成人久久av| 国产精品久久久久久久久久久久久久| 国产精品久久久久aaaa九色| 久热99视频在线观看| 北条麻妃在线一区二区| 欧美激情xxxxx| 欧美精品一区三区| www.xxxx欧美| 欧美中文在线视频| 亚洲娇小xxxx欧美娇小| 欧美最猛性xxxxx亚洲精品| 久久精品小视频| 欧美日韩高清在线观看| 久久久精品一区二区| 国产精品国产自产拍高清av水多| 国产精品视频中文字幕91| 亚洲精品美女免费| 欧美大尺度在线观看| 亚洲网址你懂得| 精品久久久一区| 亚洲日本中文字幕免费在线不卡| 欧美成人国产va精品日本一级| 国产精品福利在线| 日韩中文字幕免费| 91久久久在线| 亚洲电影免费观看高清完整版在线| 亚洲欧美综合图区| 成人免费观看网址| 色中色综合影院手机版在线观看| 国产精品av在线播放| 亚洲乱码国产乱码精品精| 久久国产精品久久国产精品| 久久精品国产精品| 97在线精品国自产拍中文| 亚洲成人精品久久| 欧美激情视频播放| 国产精品无av码在线观看| 69久久夜色精品国产69乱青草| 俺去了亚洲欧美日韩| 91在线免费观看网站| 日韩高清av在线| 欧美在线一级va免费观看| 久久久国产视频| 亚洲色图欧美制服丝袜另类第一页| 国产在线观看不卡| 91精品视频大全| 亚洲xxxx视频| y97精品国产97久久久久久| 91久久精品美女高潮| 久久这里有精品视频| 成年人精品视频| 亚洲人成网站777色婷婷| 亚洲欧美日韩高清| 国产精品黄页免费高清在线观看| 精品成人国产在线观看男人呻吟| 茄子视频成人在线| 性色av一区二区三区红粉影视| 久青草国产97香蕉在线视频| 欧美美最猛性xxxxxx| 国产精品一区二区久久久| 亚洲毛茸茸少妇高潮呻吟| 日本一区二区在线播放| 国产精品视频久久久久| 在线观看国产成人av片| 国产精品久久久久久婷婷天堂| 日本在线精品视频| 色狠狠久久aa北条麻妃| 国产精品日韩欧美综合| 国产成人亚洲综合青青| 欧美wwwwww| 成人在线激情视频| 久久人人爽人人爽人人片av高清| 欧美精品久久久久久久| 国产精品久久久久久久久久久久久久| 欧美在线观看视频| 欧美自拍视频在线观看| 欧美日韩中文字幕综合视频| 中文字幕一区二区三区电影| 国产亚洲欧洲高清| 亚洲伊人成综合成人网| 欧美亚洲国产日本| 欧洲成人免费视频| 伊人伊成久久人综合网站| www.xxxx精品| 两个人的视频www国产精品| 亚洲欧美色婷婷| 日韩高清av在线| 91精品久久久久久| 日韩在线不卡视频| 久久久久久久久久国产精品| 日韩av中文在线| 美女撒尿一区二区三区| 91av视频在线免费观看| 久久在线精品视频| 亚洲va欧美va国产综合剧情| 亚洲欧美制服综合另类| 欧美理论电影在线观看| 国产成人精品av在线| 国产精品一二三在线| 亚洲欧美在线免费观看| 成人深夜直播免费观看| 欧美精品xxx| 国产日韩欧美中文在线播放| 欧美精品电影免费在线观看| 欧美丰满少妇xxxxx| 日韩男女性生活视频| 国产成+人+综合+亚洲欧美丁香花| 久久综合久久88| 中文字幕日韩精品在线观看| 精品中文字幕在线2019| 亚洲综合视频1区| 国产午夜一区二区| 亚洲欧美福利视频| 5566成人精品视频免费| 亚洲欧美色图片| 亚洲黄页视频免费观看| 91av国产在线| 久久精品国产亚洲| 欧美午夜精品久久久久久久| 欧美性在线观看|