Rust使用Sauron實現Web介面互動

二次元攻城狮發表於2024-03-25

目錄
  • 簡介
    • 架構
    • Application 和元件
  • 簡單入門示例
    • 先決條件
    • 建立專案
    • 編譯庫檔案
    • 引用庫檔案
    • 執行專案
  • 介面互動示例
    • 建立專案
    • 編譯庫檔案
    • 實現應用函式
    • 引用庫檔案
    • 執行專案
  • 參考資料

簡介

Sauron 是一個多功能的 Web 框架和庫,用於構建客戶端和/或伺服器端 Web 應用程式,重點關注人體工程學、簡單性和優雅性。這使您可以編寫儘可能少的程式碼,並更多地關注業務邏輯而不是框架的內部細節。
github:https://github.com/ivanceras/sauron
文件:https://sauron-rs.github.io/

架構

Sauron 遵循模型-檢視-更新架構(也稱為 Elm 架構),它總是分為三個部分:

  • 模型 - 應用程式的狀態
  • 檢視 - 一種將狀態轉換為 HTML 的方法
  • 更新 - 一種根據訊息更新狀態的方法

Application 和元件

為了使模型在 Sauron 程式中執行,它必須實現 Application trait,然後定義下面兩個函式:

  • view 函式:該函式告訴程式如何顯示模型。
  • update 函式:該函式描述如何根據訊息更新模型狀態。

簡單入門示例

先決條件

確保已安裝所有必備元件:

  • rust and cargo:Rust基礎環境和工具。
  • wasm-pack:將 rust 程式碼編譯到 webassembly 中,然後放入 ./pkg 目錄中。
  • basic-http-server:在本地提供靜態檔案。

執行以下命令安裝wasm-pack

cargo install wasm-pack

執行以下命令安裝basic-http-server

cargo install basic-http-server

wasm-pack 預設會使用 wasm-opt 工具進行大小最佳化,而這個工具也是執行時下載安裝的。下載 wasm-opt 使用的是 github 連結,國內環境大機率是下載失敗的,可以參考 如何安裝WASM-OPT? 手動下載 wasm-opt.exe 後放到 .cargo\bin路徑下。

建立專案

建立一個名為 hello 的新專案:

cargo new --lib hello

在 Cargo.toml 中指定這個 crate 需要編譯為 cdylib動態系統庫):

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

執行以下命令,新增sauron作為專案的依賴項。

cargo add sauron

編譯庫檔案

修改 src/lib.rs程式碼,在段落中顯示“hello”文字:

use sauron::{node, wasm_bindgen, Application, Cmd, Node, Program};

struct App;

impl Application<()> for App {
    fn view(&self) -> Node<()> {
        node! {
            <p>
                "hello"
            </p>
        }
    }

    fn update(&mut self, _msg: ()) -> Cmd<Self, ()> {
        Cmd::none()
    }
}

//函式應該在載入 wasm 模組時自動啟動,類似於 main 函式
#[wasm_bindgen(start)]
pub fn main() {
    Program::mount_to_body(App::new());
}
  • view 方法中使用 node! 宏,採用類似 html 的語法來顯示應用程式。
  • 為 App 實現 Application 特徵,實現必要的方法來告訴 sauron 應用程式的行為。
  • 這裡的 main 函式在載入 wasm 模組時自動啟動,函式可以任意命名,只要配置 start 即可。

執行以下命令進行編譯:

wasm-pack build --release --target=web

編譯時間稍微有點長,wasm-pack 會在專案中建立一個資料夾 ./pkg,裡面包含生成的編譯檔案,只需要關注其中 2 個檔案:

hello.js
hello_bg.wasm

它們的名稱派生自給定的包名稱 <package_name>.js<package_name>_bg.wasm

引用庫檔案

在專案的根目錄中建立 index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <script type=module>
      import init from './pkg/hello.js';
      await init().catch(console.error);
    </script>
  </body>
</html>
  • 使用的是 <script type=module>, 從 ./pkg 資料夾中引用了 ./pkg/hello.js
  • 在後臺,./pkg/hello.js 將負責在後臺載入 ./pkg/hello_bg.wasm

執行專案

重新編譯webapp,每次對 rust 程式碼進行更改時發出此命令。

wasm-pack build --release --target=web

最後使用 basic-http-server 提供檔案:

basic-http-server

預設情況下,它在埠 4000 中提供頁面,導航到 http://127.0.0.1:4000 以檢視“hello”訊息。

介面互動示例

在瀏覽器中顯示 3 個按鈕,單擊這些按鈕可以增加/減少和重置計數。

建立專案

建立一個名為 counter 的新 rust 庫專案:

cargo new --lib counter

接下來修改 crate 型別為 “cdylib” 庫 ,新增 sauron 依賴,這裡不在贅述。

編譯庫檔案

在 src/lib.rs 中放入此程式碼:

use sauron::prelude::*;
use sauron::node;

struct App {
    count: i32,
}

//新增了一個函式 new 來建立以 count 0 開頭的初始狀態 App
impl App {
    fn new() -> Self {
        App { count: 0 }
    }
}

定義應用程式將具有的一組操作:

enum Msg {
    Increment,
    Decrement,
    Reset,
}

為模型 App 實現 Application 特徵:

impl Application<Msg> for App {
    fn view(&self) -> Node<Msg> {
        node! {
            <main>
                <input type="button"
                    value="+"
                    on_click=|_| {
                        Msg::Increment
                    }
                />
                <button class="count" on_click=|_|{Msg::Reset} >{text(self.count)}</button>
                <input type="button"
                    value="-"
                    on_click=|_| {
                        Msg::Decrement
                    }
                />
            </main>
        }
    }

    fn update(&mut self, msg: Msg) -> Cmd<Self, Msg> {
        match msg {
            Msg::Increment => self.count += 1,
            Msg::Decrement => self.count -= 1,
            Msg::Reset => self.count = 0,
        }
        Cmd::none()
    }
}
  • view 方法返回型別為 Node<Msg>,這意味著它建立一個 html 節點,其中它的任何成員 html 元素都有一個事件偵聽器,該事件偵聽器可以向程式處理程式發出 Msg 訊息
  • update 方法接受 Msg 作為引數,並根據 Msg 的變體修改模型 App。

實現應用函式

接下來為 wasm Web 應用定義入口點,透過使用 #[wasm_bindgen(start)] 註釋公共函式來完成:

#[wasm_bindgen(start)]
pub fn start() {
    Program::mount_to_body(App::new());
}

為了演示純函式互動,這裡再新增一個簡單的加法函式:

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
  a + b
}

如果只需要js呼叫Rust的函式,那隻需要新增 wasm_bindgen 依賴即可,參考 使用Rust和WebAssembly整花活兒(一)——快速開始

引用庫檔案

在專案基本資料夾的 index.html 檔案中連結應用,可以像往常一樣放置樣式:

<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
    <title>Counter</title>
    <style type="text/css">
        body { font-family: verdana, arial, monospace; }
        main {
            width:30px;
            height: 100px;
            margin:auto;
            text-align: center;
        }
        input, .count{
            font-size: 40px;
            padding: 30px;
            margin: 30px;
        }
    </style>
    <script type=module>
        import init, { add } from './pkg/counter.js';
        await init().catch(console.error);
        const result = add(1, 2);
        console.log(`the result from rust is: ${result}`);
    </script>
  </head>
  <body>
  </body>
</html>

注意上面的 import init, { add } from ,add 函式在使用前需要匯入,否則會呼叫失敗。

執行專案

編譯庫檔案:

wasm-pack build --release --target=web

啟動靜態站點:

basic-http-server

訪問 http://127.0.0.1:4000 檢視效果:
image

參考資料

  • 如何安裝WASM-OPT?
  • 國內網路環境下配置 wasm 開發環境
  • 使用Rust和WebAssembly整花活兒(一)——快速開始

相關文章