如果你有一些 Rust 代碼,你可以將它編譯成WebAssembly (wasm)。本教程將帶您了解將 Rust 項目編譯爲 wasm 並在現有 Web 應用程序中使用它所需的所有知識。
Rust 和 WebAssembly 用例
Rust 和 WebAssembly 有兩個主要用例:
構建一個完整的應用程序——一個基於 Rust 的整個 Web 應用程序。
構建應用程序的一部分——在現有的 JavaScript 前耑中使用 Rust。
目前,Rust 團隊專注於後一種情況,這就是我們在這裡介紹的内容。對於前一種情況,請查看以下項目yew.
在本教程中,我們使用 構建一個包wasm-pack,這是一個在 Rust 中構建 JavaScript 包的工具。這個包將只包含 WebAssembly 和 JavaScript 代碼,因此包的用戶不需要安裝 Rust。他們甚至可能沒有注意到它是用 Rust 編寫的。
Rust 環境設置
讓我們完成所有必需的步驟來設置我們的環境。
安裝 Rust
轉到安裝 Rust安裝 Rust頁並按照說明進行操作。這會安裝一個名爲“rustup”的工具,它可以讓你管理多個版本的 Rust。默認情況下,它會安裝最新的穩定版 Rust,您可以將其用於一般的 Rust 開發。Rustup 安裝rustc,Rust 編譯器,以及cargoRust 的包管理器,rust-stdRust 的標準庫,以及一些有用的文档 — rust-docs.
注意:注意bin系統中需要cargo目錄的安裝後說明PATH。這是自動添加的,但您必須重新啓動終耑才能使其生效。
wasm包
要構建包,我們需要一個額外的工具,wasm-pack. 這有助於將代碼編譯爲 WebAssembly,並生成正確的打包以供在瀏覽器中使用。要下載並安裝它,請在終耑中輸入以下命令:
$ cargo install wasm-pack
構建我們的 WebAssembly 包
讓我們在 Rust 中創建一個新包。導航到您保存個人項目的位置,然後輸入:
$ cargo new --lib hello-wasm Created library `hello-wasm` project
這將在一個名爲的子目錄中創建一個新庫,其中hello-wasm包含您需要的所有内容:
+-- Cargo.toml
+-- src
+-- lib.rs
首先,我們有Cargo.toml;這是我們用來配置構建的文档。如果您使用Gemfile過 Bundler 或package.jsonnpm,這可能很熟悉;Cargo 的工作方式與他們兩者相似。
接下來,Cargo 爲我們生成了一些 Rust 代碼src/lib.rs:
#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
請把這個小屎山刪除, 寫一些Rust程序
將這段代碼放入src/lib.rs:
use wasm_bindgen::prelude::*; #[wasm_bindgen] extern { pub fn alert(s: &str); } #[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
這是我們 Rust 項目的内容。它有三個主要部分;讓我們依次討論它們。我們在這裡給出一個高層次的解釋,並掩飾一些細節;要了解有關 Rust 的更多信息,請查看免費在線書籍Rust 編程語言.
用於Rustwasm-bindgen和 JavaScript 之間的通信
第一部分如下所示:
use wasm_bindgen::prelude::*;
庫在 Rust 中稱爲“crates”。
這個套路你懂否? Cargo裝的都是crates.
第一行包含一個use命令,它將庫中的代碼導入到您的代碼中。在這種情況下,我們將導入wasm_bindgen::prelude模塊中的所有内容。我們將在下一節中使用這些功能。
在我們進入下一節之前,我們應該多談談wasm-bindgen.
wasm-pack使用wasm-bindgen另一個工具,在 JavaScript 和 Rust 類型之間架起一座橋梁。它允許 JavaScript 使用字符串調用 Rust API,或調用 Rust 函數來捕獲 JavaScript 異常。
我們在我們的包中使用wasm-bindgen's 功能。事實上,這是下一節。
從 Rust 調用 JavaScript 中的外部函數
下一部分如下所示:
#[wasm_bindgen] extern { pub fn alert(s: &str); }
内部的位#[ ]稱爲“屬性”,它以某種方式修改下一條語句。在這種情況下,該語句是一個extern,它告訴 Rust 我們想要調用一些外部定義的函數。該屬性表示“wasm-bindgen 知道如何找到這些函數”。
第三行是函數簽名,用 Rust 編寫。它說“alert函數接受一個參數,一個名爲 . 的字符串s。”
您可能會懷疑,這是JavaScript 提供的alert功能。我們在下一節中調用這個函數。
每當您想調用 JavaScript 函數時,您都可以將它們添加到此文档中,並wasm-bindgen爲您完成所有設置。並非所有内容都受支持,但我們正在努力。請文档錯誤如果缺少某些東西。
生成 JavaScript 可以調用的 Rust 函數
最後一部分是這個:
#[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
再一次,我們看到了#[wasm_bindgen]屬性。在這種情況下,它不是修改一個extern塊,而是一個fn; 這意味著我們希望這個 Rust 函數能夠被 JavaScript 調用。它的反面extern:這些不是我們需要的功能,而是我們提供給世界的功能。
這個函數被命名爲greet,並接受一個參數,一個字符串(寫入&str),name。然後它調用我們在上面的塊中要求的alert函數。extern它傳遞一個對format!宏的調用,它允許我們連接字符串。
在這種format!情況下,宏接受兩個參數,一個格式字符串和一個要放入其中的變量。格式字符串是"Hello, {}!"位。它包含{}s,其中將插入變量。我們傳遞的變量是name函數的參數,所以如果我們調用greet("Steve")我們應該看到"Hello, Steve!".
這被傳遞給alert(),所以當我們調用這個函數時,我們會看到一個帶有“你好,史蒂夫!”的警告框。在裡面。
現在我們的庫已經寫好了,讓我們來構建它。
將我們的代碼編譯爲 WebAssembly
爲了正確編譯我們的代碼,我們首先需要使用Cargo.toml. 打開此文档,並將其内容更改爲如下所示:
[package] name = "hello-wasm"version = "0.1.0" authors = ["Your Name <you@example.com>"] description = "A sample project with wasm-pack" license = "MIT/Apache-2.0" repository = "https://github.com/yourgithubusername/hello-wasm"edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
git填寫您自己的存儲庫並使用與該authors字段相同的信息。
要添加的重要部分是[package]. 該[lib]部分告訴 Rust 構建cdylib我們包的一個版本;我們不會在本教程中討論這意味著什麽。更多信息,請諮詢貨物和鏽連杆文档。
最後一節是[dependencies]節。這裡是我們告訴 Cargowasm-bindgen我們想要依賴哪個版本的地方;在這種情況下,這是任何0.2.z版本(但不是0.3.0或更高版本)。
構建包
現在我們已經完成了所有設置,讓我們構建包。在終耑中輸入:
$ wasm-pack build --target web
這會做很多事情(它們會花費很多時間,尤其是第一次運行時wasm-pack)。要詳細了解它們,請查看這篇關於 Mozilla Hacks 的博文. 簡而言之,wasm-pack build:
將你的 Rust 代碼編譯爲 WebAssembly。
在該 WebAssembly 上運行wasm-bindgen,生成一個 JavaScript 文档,該文档將該 WebAssembly 文档包裝到瀏覽器可以理解的模塊中。
創建一個pkg目錄並將該 JavaScript 文档和您的 WebAssembly 代碼移動到其中。
讀取您的Cargo.toml並生成等效的package.json.
README.md將您的(如果有的話)複制到包裝中。
最終結果?您在pkg目錄中有一個包。
關於代碼大小的題外話
如果您查看生成的 WebAssembly 代碼大小,它可能是幾百 KB。我們根本沒有指示 Rust 優化大小,這樣做會大大減少大小。這超出了本教程的範圍,但是如果您想了解更多信息,請查看 Rust WebAssembly 工作組的文档縮小 .wasm 大小.
在網路上使用包
現在我們已經編譯好了 wasm 模塊。讓我們在瀏覽器中運行它。
index.html讓我們首先在項目的根目錄中創建一個名爲的文档,並爲其提供以下内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello-wasm example</title> </head> <body> <script type="module"> import init, {greet} from "./pkg/hello_wasm.js"; init().then(() => { greet("WebAssembly") }); </script> </body> </html>
該文档中的腳本會導入js膠水代碼,初始化wasm模塊,調用greet我們在rust中寫的函數。
使用本地 Web 服務器爲項目的根目錄提供服務(例如python3 -m http.server)。如果您不確定如何執行此操作,請參閲運行簡單的本地 HTTP 服務器。
注意:確保使用支持application/wasmMIME 類型的最新 Web 服務器。較舊的 Web 服務器可能還不支持它。
從 Web 服務器加載index.html, 舉個例子, 如果您電腦裡支持python3, 則在代碼根目錄路徑用命令行輸入
python3 -m http.server
進到locahost:8000耑口頁面, 屏幕上會出現一個警告框,其中包含Hello, WebAssembly!。我們已經成功地從 JavaScript 調用到 Rust,並從 Rust 調用到 JavaScript。
使我們的包對 npm 屎山系統可用
如果你想將 WebAssembly 模塊與 npm 屎山一起使用,我們需要進行一些更改。
讓我們從使用 target bundler 選項重新編譯 Rust 開始:
$ wasm-pack build --target bundler
安裝 Node.js 和npm屎山管理
我們正在構建一個 npm 包,因此您需要安裝 Node.js 和 npm。
要獲取 Node.js 和 npm,請轉到獲取 npm!頁面並按照說明進行操作。選擇版本時,請選擇您喜歡的任何版本;本教程不是特定於版本的。
接下來,讓我們使用 `npm link` 使這個包對安裝的其他 JavaScript 包可用
$ cd pkg $ npm link
我們現在有一個 npm 包,用 Rust 編寫,但編譯爲 WebAssembly。它可以從 JavaScript 中使用,並且不需要用戶安裝 Rust;包含的代碼是 WebAssembly 代碼,而不是 Rust 源代碼。
在網路上使用npm屎山構造包
讓我們建立一個使用我們新的npm屎山構造包的網站。許多人通過各種打包工具使用 npm 包,我們將webpack在本教程中使用其中之一。它只是有點複雜,並顯示了一個現實的用例。
讓我們移出該pkg目錄,並創建一個新目錄 ,site來嘗試一下:
$ cd ..$ mkdir site $ cd site $ npm link hello-wasm
創建一個新文档 ,package.json並將以下代碼放入其中:
{ "scripts": { "serve": "webpack-dev-server" }, "dependencies": { "hello-wasm": "^0.1.0" }, "devDependencies": { "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.10" } }
接下來,我們需要配置 Webpack。創建webpack.config.js並放入以下内容:
const path = require('path'); module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "index.js", }, mode: "development" };
接下來,創建一個名爲 的文档index.js,並爲其提供以下内容:
import("./node_modules/hello-wasm/hello_wasm.js").then((js) => { js.greet("WebAssembly with NPM");});
node_modules這將從文档夾中導入新模塊。這不被認爲是最佳實踐,但這是一個演示,所以現在可以了。加載後,它會greet從該模塊調用函數,並"WebAssembly"作爲字符串傳遞。請注意這裡沒有什麽特別之處,但我們正在調用 Rust 代碼。就 JavaScript 代碼而言,這只是一個普通的模塊。
最後,我們需要修改HTML文档;打開index.html文档並將當前内容替換爲以下内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello-wasm example</title> </head> <body> <script src="./index.js"></script> </body> </html>
我們完成了制作文档。讓我們試一試:
$ npm install $ npm run serve
這將啓動一個 Web 服務器。加載http://localhost:8080並在屏幕上出現一個警告框,其中包含Hello, WebAssembly with NPM!。我們已經成功地將 Rust 模塊與 npm 一起使用。
結論
我們的教程到此結束;我們希望您發現它有用。