2021-05-06 WebAssembly 第一天

2021-05-06 WebAssembly

開發(fā)環(huán)境

操作系統(tǒng)

目前用的操作系統(tǒng)是windows。

Node

開發(fā)環(huán)境 。

先下載一個Nodejs。

傳送門

當(dāng)前版本: 14.16.1

[圖片上傳失敗...(image-842b47-1620292429591)]

Rust

這個就不介紹怎么安裝了,

我的版本是

[圖片上傳失敗...(image-8913b1-1620292429591)]

教程參考

編譯成WebAssembly 有兩個教程。

官網(wǎng) mozilla官方教程

還有另外一個是 ,不過這個用的是他們自己寫的一個構(gòu)建工具。

secondstate 出品的SSVM

還有一本書,建議深入的時候讀。上面教程都是非常簡單的。書比較系統(tǒng)

rustwasm

動手

我們先按官方教程擼一把。

在本教程中,我們將使用 Rust 的 npm 包構(gòu)建工具 wasm-pack 來構(gòu)建一個 npm 包。這個包只包含 WebAssembly 和 JavaScript 代碼,以便包的用戶無需安裝 Rust 就能使用。他們甚至不需要知道這里包含 WebAssembly!

安裝wasm-pack

要構(gòu)建我們的包,我們需要一個額外工具 wasm-pack。它會幫助我們把我們的代碼編譯成 WebAssembly 并制造出正確的 npm 包。使用下面的命令可以下載并安裝它:

$ cargo install wasm-pack

構(gòu)建我們的 WebAssembly npm 包

萬事俱備,來創(chuàng)建一個新的 Rust 包吧。打開你用來存放私人項目的目錄,我的目錄是 d:/workspace:

$ cargo new --lib hello-wasm

[圖片上傳失敗...(image-eca9bb-1620292429591)]

這里會在名為 hello-wasm 的子目錄里創(chuàng)建一個新的庫,里面有下一步之前我們所需要的一切:

+-- Cargo.toml
+-- src
    +-- lib.rs

首先,我們有一個 Cargo.toml 文件,這是我們配置構(gòu)建的方式。如果你用過 Bundler 的 Gemfile 或者 npm 的 package.json,你應(yīng)該會感到很熟悉。Cargo 的用法和它們類似。

接下來,Cargo 在 src/lib.rs 生成了一些 Rust 代碼:

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

我們完全不需要使用這些測試代碼,所以繼續(xù)吧,我們刪掉它。

修改Cargo.toml

接下來不太按教程的套路走了,如果還按教程的套路,那么不如直接自己看教程好了。

首先我們需要去修改 Cargo.toml 添加下列內(nèi)容

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

第一個部分 — [lib] — 告訴 Rust 為我們的包建立一個 cdylib 版本;官方解釋

  • --crate-type=cdylib, #[crate_type = "cdylib"] - A dynamic system library will be produced. This is used when compiling a dynamic library to be loaded from another language. This output type will create *.so files on Linux, *.dylib files on macOS, and *.dll files on Windows.

第二個部分是 [dependencies] 部分。在這里我們告訴 Cargo 我們需要依賴哪個版本的 wasm-bindgen ;

[圖片上傳失敗...(image-156bc0-1620292429591)]

我們到crate.io 上看一下,最新版本還是0.2.73

寫點RUST代碼吧。

讓我們在 src/lib.rs 寫一些代碼替換掉原來的:

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

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

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

這就是我們這個 Rust 項目的內(nèi)容。它有三個主要部分,讓我們按順序來講。

使用 wasm-bindgen 在 Rust 與 JavaScript 之間通信

第一部分看起來像這樣:

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

第一行就像在說 “哇 Rust,我們在用一個叫做 wasm_bindgen 的庫”。在 Rust 當(dāng)中,庫被稱為 “crates”,因為我們使用的是一個外部庫,所以有 "extern"。通過 extern crate + crate名稱將包內(nèi)的模塊引入當(dāng)前環(huán)境。

在使用 extern 引入包之后,可以用 use 將模塊引入到當(dāng)前作用域。

第三行包括了一個將庫中的代碼引入到你的代碼中的使用命令。在這個情況下,將會引入 wasm_bindgen::prelude 的全部模塊。

在我們開始下一節(jié)之前,我們將講一講 wasm-bindgen.

wasm-pack 使用 wasm-bindgen,其它工具,去提供一個連接 JavaScript 和 Rust 的橋。它允許 JavaScript 使用 string 調(diào)用 Rust API,或者調(diào)用一個 Rust function 去捕獲 JavaScript 異常。

我們將在我們的包中使用 wasm-bindgen 的功能。事實上,這是下一節(jié)的內(nèi)容!

在 Rust 中調(diào)用來自 JavaScript 的外部函數(shù)

接下來的部分看起來像這樣:

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

#[] 中的內(nèi)容叫做 "屬性",并以某種方式改變下面的語句。在這種情況下,下面的語句是一個 extern,它將告訴 Rust 我們想調(diào)用一些外部定義的函數(shù)。這個屬性告訴我們 "wasm-bindgen 知道如何找到這些函數(shù)"。

第三行是用 Rust 寫的函數(shù)簽名。它告訴我們 "alert 函數(shù)接受一個叫做 s 的字符串作為參數(shù)。"

你可能會疑惑這個函數(shù)是什么,你的疑惑可能是正確的:這是the alert function provided by JavaScript!

編寫能夠在JavaScript中調(diào)用的 Rust 函數(shù)

最后一部分是這樣的:

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

我們又看到了 #[wasm_bindgen] 屬性。在這里,它并非定義一個 extern 塊,而是 fn,這代表我們希望能夠在 JavaScript 中使用這個 Rust 函數(shù)。 這和 extern 正相反:我們并非引入函數(shù),而是要把函數(shù)給外部世界使用。

這個函數(shù)的名字是 say,它需要一個參數(shù),一個字符串 (寫作 &str)。它調(diào)用了我們前面在 extern 塊中引入的 alert 函數(shù)。它傳遞了一個讓我們串聯(lián)字符串的 format! 宏的調(diào)用。

在這里有兩個參數(shù),一個格式化字符串和一個要填入的變量。格式化字符串是 "Hello, {}!" 部分。它可以包含一個或多個 {},變量將會被填入其中。傳遞的變量是 name,也就是這個函數(shù)的參數(shù)。所以當(dāng)我們調(diào)用 greet("Steve")時我們就能看到 "Hello, Steve!"。

這個傳遞到了 alert(),所以當(dāng)我們調(diào)用這個函數(shù)時,我們應(yīng)該能看到他談彈出了一個帶有 "Hello, Steve!" 的消息框。

我們的庫寫完了,是時候構(gòu)建它了。

構(gòu)建包

現(xiàn)在我們已經(jīng)完成了所有配置項,開始構(gòu)建吧!在命令行輸入以下命令:

$ wasm-pack build 
  • wasm-pack build, 構(gòu)建生成 pkg 文件夾;
  • wasm-pack test 用于測試

這個命令將做一系列事情 (這會花一些時間,特別是當(dāng)你第一次運(yùn)行 wasm-pack)。想了解詳細(xì)情況,查看這篇在 Mozilla Hacks 上的文章。簡單來說,wasm-pack build 將做以下幾件事:

  1. 將你的 Rust 代碼編譯成 WebAssembly。
  2. 在編譯好的 WebAssembly 代碼基礎(chǔ)上運(yùn)行 wasm-bindgen,生成一個 JavaScript 文件將 WebAssembly 文件包裝成一個模塊以便 npm 能夠識別它。
  3. 創(chuàng)建一個 pkg 文件夾并將 JavaScript 文件和生成的 WebAssembly 代碼移到其中。
  4. 讀取你的 Cargo.toml 并生成相應(yīng)的 package.json。
  5. 復(fù)制你的 README.md (如果有的話) 到文件夾中。

[圖片上傳失敗...(image-438fd-1620292429591)]

[圖片上傳失敗...(image-29f44-1620292429591)]

                        .......

[圖片上傳失敗...(image-359e3a-1620292429591)]

第一次編譯非常非常多東西.....

最后的結(jié)果?你在 pkg 文件夾下有了一個 npm 包。

我們看看里面有什么東西

[圖片上傳失敗...(image-4eb720-1620292429591)]

對代碼體積的一些說明

如果你檢查生成的 WebAssembly 文件體積,它可能有幾百 kB。我們沒有讓 Rust 去壓縮生成的代碼,從而大大減少生成包的體積。這和本次教程主題無關(guān),但如果你想了解更多,查看 Rust WebAssembly 工作組文檔上關(guān)于 減少 .wasm 體積 的說明。真的有用的傳送門

我們先看看現(xiàn)在編譯出來的wasm有多大?

[圖片上傳失敗...(image-522d02-1620292429591)]

然后我們按照教程上的

Cargo.toml里面進(jìn)行修改

[profile.release]
lto = true

這為 LLVM 提供了更多內(nèi)聯(lián)和修剪功能的機(jī)會。它不僅會變小,而且還會使它在運(yùn)行時間更快!缺點是編譯需要更長的時間。

告訴LLVM優(yōu)化尺寸而不是速度

LLVM 的優(yōu)化通行證經(jīng)過調(diào)整,以在默認(rèn)情況下提高速度,而不是尺寸。我們可以通過將該部分修改為:

[profile.release]
lto = true
opt-level = 's'

[圖片上傳失敗...(image-654412-1620292429591)]

有效果哦??!

或者,更積極地優(yōu)化尺寸,進(jìn)一步的潛在速度成本:

[profile.release]
lto = true
opt-level = 'z'

[圖片上傳失敗...(image-b7c211-1620292429591)]

多了一點....

我們建立一個使用我們包的網(wǎng)站

讓我們建立一個使用我們包的網(wǎng)站! 人們通過各種打包工具使用 npm包,在本教程中,我們將使用 webpack。 它比其他某些打包工具稍微復(fù)雜一點,但展示了更實際的用法。

讓我們離開pkg目錄,并創(chuàng)建一個新目錄site,嘗試以下操作:

創(chuàng)建一個新文件 package.json,然后輸入如下代碼:

{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "dependencies": {
    "@mynpmusername/hello-wasm": "^0.1.0"
  },
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

我們沒有像教程那樣上傳到npm中。我們需要對 dependencies 進(jìn)行修改一下。指向相對地址

  • dependencies: 表示生產(chǎn)環(huán)境下的依賴管理;無論開發(fā)還是生產(chǎn)都會下載使用的依賴
  • devDependencies: 表示開發(fā)環(huán)境下的依賴管理;
{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "dependencies": {
    "hello_wasm": "file:../pkg"
  },
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

接下來,我們需要配置Webpack。 創(chuàng)建 webpack.config.js 并輸入:

const path = require('path');
module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
  mode: "development"
};

現(xiàn)在我們需要一個HTML文件。 創(chuàng)建一個index.html并寫入如下內(nèi)容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>hello-wasm example</title>
  </head>
  <body>
    <script src="./index.js"></script>
  </body>
</html>

最后,從HTML文件中引用index.js

const js = import("../pkg/hello_wasm.js");
js.then(js => {
  js.say("WebAssembly");
});

這將從pkg文件夾導(dǎo)入我們的模塊。這不是最佳做法,但這里只做一個演示,因此暫時就這樣用。 加載后,它將從該模塊調(diào)用say函數(shù),并傳入字符串“WebAssembly”參數(shù)。注意這里看上去沒有什么特別的,但是我們正在調(diào)用 Rust 代碼! 就JavaScript代碼所知,這只是一個普通模塊。

我們已經(jīng)完成了所有的文件! 讓我們試一下:

$ npm install
$ npm run serve

[圖片上傳失敗...(image-a4d2b-1620292429591)]

我們看一下。寫的wasm加載到了前端了。

[圖片上傳失敗...(image-1019c7-1620292429591)]

打完收工

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容