Rust 跨界前端全攻略

作者 | Diomidis Spinellis

譯者 | 馬超     責編 | 張紅月

出品 | CSDN(ID:CSDNnews)

Nicolas Frankel 一直是我非常關注的 Rust 技術專欄作者之一,近期我看到他的一篇有關於 Rust 與 JS 結合搭建 Serverless WebAssembly 的文章,立刻感覺眼前一亮,這篇文章當中沒有那些繁鎖的權限傳遞機制、智能指針等項目,只要跟着作者的代碼樣例進行模仿就行了,而且這方面的知識也比較實用,不失爲一篇在學習中途中可以放鬆一下轉換心情的好文章,下面就帶大家奇文共賞。

我們知道 JavaScript 是前端方面唯一個算得上是通用的語言,各種前端流行框架本質上都是基於 JavaScript。雖然爲前端生的 JavaScript 存在很多儲如性能、併發等方面的先天不足,但是也應該看到 JavaScript 開發者社羣還在大幅增長,圍繞前端 JavaScript 的生態也是日益繁榮,而且前端技術變化很快,幾年前還稱霸一時的 Flash 幾乎直線墜落,完全被 H5 幹掉,從 NodeJS、DENO 到 Vue.js,各種新框架也是不斷湧現,也讓大家應接不暇,可以說 JS 體系的繁榮是有目共睹的。

雖然有關語言優劣的爭論大多是沒有作何實際意義的,但不可否認的是 JavaScript 往往處在編程語言鄙視鏈的底端,很多程序員都認爲 JS 之所以能倖存下來,是因爲它將執行腳本代碼的責任從服務器轉移到客戶端,這樣就減輕了服務器上的很大壓力。不過相對而言,客戶端的壓力卻因此大大增加了,想提升上網體驗前端程序員幾乎只能建議用戶去購買功能更強大而且價格昂貴的手機、PAD 或者 PC。而想要優化 JavaScript 引擎似乎還得靠 Rust 才能做到。在介紹下面的方案之前,我們先來認識一下 WebAssembly。

WebAssembly(縮寫爲 Wasm)是一種用於基於堆棧的虛擬機的二進制指令格式。Wasm 被設計爲編程語言的可移植編譯目標,支持在 Web 上部署客戶端和服務器應用程序。

總得來說 Wasm 並非要取代 JavaScript,而是指在提高前後端交互的整體性能。儘管 Rust 多用於後端,但它的特性確實有助於提升 WebAssembly 的編譯、啓動及運行速度,下面我們就一起來感受一下 Rust+Wasm 的強大威力。

Rust 和 WebAssembly


第一個 Rust 項目

我們第一個步驟側重於讓大家瞭解設置方法,這是一個 Ctrl+C、Ctrl+V 式複製粘貼項目。這個項目利用一個高效的 Cargo 插件 cargo-generate 來提升項目管理效率,它允許使用現有的 Git 存儲庫作爲模板來創建新項目。在本例中,模板是一個待編譯的 Wasm Rust 項目。具體項目的樹形結構如下:

這是非常典型的 Rust 項目結構。現在我們來看一下 Cargo.toml 文件。

[package]                                              
name = "wasm-game-of-life"
version = "0.1.0"
authors = ["Nicolas Frankel <nicolas@frankel.ch>"]
edition = "2018"
[lib]                                                  
crate-type = ["cdylib", "rlib"]                        
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.63"                                
# Rest of the file omitted for clarity purposes

這裏 Cargo.toml 其實發揮了前端項目中 pom.xml 的作用,這裏列出有關包、依賴項、編譯提示等的元信息,並定義與 Wasm 的依賴關係。當然截止目前這個項目還不是很有趣,不過我們會慢慢來建立一個項目,使 Wasm 的 Rust 代碼能夠高效交互。

接下來讓運行命令:

npm init wasm-app www

你會看到以下輸出結構:

wasm-game-of-life/
└── www/
    ├── package.json                   
    ├── webpack.config.js               
    ├── index.js                       
    ├── bootstrap.js                   
    └── index.html

其中 webpack.config.js 是調用 Wasm 代碼的入口點,index.js 是異步加載器包裝器。進行完上述步驟後,接下來只要我們完成以下四步,就可以執行整個 Wasm 代碼鏈。

  1. 將 Rust 代碼編譯爲 Wasm

  2. 生成 JavaScript 適配器代碼

  3. 安裝 NPM 依賴項 npm install

  4. 執行 npm run start

瀏覽到 http://localhost:8080 會顯示一條簡單的 alert() 消息。

在帶着廣大讀者們重新做一遍教程之前,筆者這裏先給出一些有意義的結論。也就是 Wsam+Rust 的結合過程中,可以歸結爲以下三步:

  1. 從 JavaScript 調用 Rust

  2. 從 Rust 調用 JavaScript

  3. 從 Rust 調用瀏覽器 API

從 JavaScript 調用 Rust

好接下來我們就一點一點學習這些步驟,要從 JavaScript 調用 Rust,需要將 Rust 代碼編譯爲 Wasm 並提供瘦 JavaScript 包裝器。在 Rust 方面具體方案如下:

#[wasm_bindgen]           
pub fn foo() {
    // do something
}

在 JavaScript 代碼方面示例如下:

import * as wasm from "hello-wasm-pack";       
wasm.foo();

將 hello-wasm-pack 包中的所有內容導入 wasm 命名空間之後,用戶就可以調用 foo() 函數了。

從 Rust 調用 JavaScript

Rust 調用 JavaScript 函數時需要通過 extern 關鍵字聲明使用外部函數接口,具體如下:

#[wasm_bindgen]
extern "C" {                                 
    #[wasm_bindgen(js_namespace = Math)]     
    fn random() -> f64;
}
#[wasm_bindgen]
fn random_boolean() -> bool {
    random() < 0.5                           
}

注意雖然這裏關鍵字是 extern "C",但這並不是 C 代碼,Rust 中這就是正確的語法,因此我們只要用它就可以了。接下來需要進行有關 js 沙箱(js-sys crate)的設置工作,如果要詳細瞭解相關內容,可以參考以下鏈接:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects

接下來需要將 js-sys 添加到 cargo.toml 當中,具體如下::

Cargo.toml

[dependencies]
js-sys = { version = "0.3.50"optional = true }  
[features]
default = ["js-sys"]

上述配置將允許以下代碼在 js 沙箱中使用:

use js_sys::Math;               
#[wasm_bindgen]
fn random_boolean() -> bool {
    Math::random() < 0.5        
}

上述代碼中的 Math.random() 語句會在在運行時實現由 rust 調 JavaScript 的目標。

從 Rust 調用瀏覽器 API


當然只是調用 JavaScript 還是不夠的,因爲很多客戶端 API,如 console.log(),需要調用瀏覽器 API。

下面是配置方案:

Cargo.toml

[dependencies]
web-sys = { version = "0.3", features = ["console"] }

配置完成後我們就可以通過以下樣例來調用瀏覽器 API:

wasm.rs

extern crate web_sys;                                 
use web_sys::console;                                 
#[wasm_bindgen]
impl Foo {
    pub fn new() -> Foo {
        utils::set_panic_hook();
        Universe {}
    }
    pub fn log(&self) {
        console::log_1("Hello from console".into());  
    }
}

結論


我們在重複一下之前的要點,在前端使用 Rust 的三大要決分別是:從 JavaScript 調用 Rust,從 Rust 調用 JavaScript,從 Rust 調用瀏覽器 API。

原文地址:https://blog.frankel.ch/start-rust/5/

源碼地址:https://github.com/ajavageek/rust-game-of-life

聲明:本文由 CSDN 翻譯,轉載請註明來源。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2lFtOr3P-d-WS9H2_N26TQ