Rust 與 WebAssembly 的結合為網頁應用程式開發帶來了新的可能性,允許開發者利用 Rust 的效能和安全性優勢構建高效率的網頁應用。本文示範的登入應用程式,即是一個典型的應用案例。透過 wasm-pack 工具,我們可以輕鬆地將 Rust 程式碼編譯成 Wasm 模組,並在 JavaScript 中呼叫。這個流程不僅簡化了開發流程,也提升了網頁應用程式的效能。文章中詳細介紹瞭如何設定開發環境、編譯 Rust 程式碼、建立網頁介面,以及整合 Rust 和 JavaScript 程式碼,讓讀者可以逐步實作一個功能完整的登入應用程式。
第19章 在網頁瀏覽器中執行Rust程式
簡介
本章將探討如何使用WebAssembly(Wasm)在網頁瀏覽器中執行Rust程式碼。我們將瞭解Wasm的基本原理及其與Rust的整合,為開發高效能和高安全性的網頁應用程式奠定基礎。在本章中,我們將介紹如何設定開發環境,利用wasm-pack和npm等工具來構建、測試和佈署Rust生成的Wasm程式碼。透過實際範例和實作練習,我們將建立從簡單的Hello-Web專案到更複雜的使用者認證系統等網頁應用程式,展示Rust和JavaScript的無縫整合,以提供強大且高效的網頁體驗。透過這次探索,讀者將獲得寶貴的見解,瞭解如何利用Rust和Wasm的結合力量來構建高效能的網頁應用程式,突破傳統網頁開發的界限。
結構
本章涵蓋以下主題:
- WebAssembly簡介
- 準備開發環境
- 建立Hello-Web應用程式
- 使用者認證網頁應用程式
目標
在本章結束時,您將具備在JavaScript程式碼中運用Rust的能力和效率,從而釋放實作接近原生效能的潛力。您將學會如何將Rust生成的Wasm二進位制檔案納入您的網頁專案中,有效地組織和利用Rust函式在您的網頁應用程式中。
WebAssembly
Wasm(通常縮寫為wasm)是一種二進位制指令格式,作為高階程式語言的可移植編譯目標。它旨在使網頁瀏覽器能夠以接近原生的速度執行,提供比傳統JavaScript更高效的替代方案,用於某些任務。本文將探討Wasm的基本原理,探討其目的、與各種程式語言的互操作性,以及其提供的效能優勢。
內容解密:
Wasm是一種二進位制格式,能夠使開發者使用多種程式語言編寫程式碼,並在網頁瀏覽器中以高效的方式執行。這使得Wasm成為開發高效能網頁應用程式的一個重要工具。Wasm的主要特點包括:
- 可移植性:Wasm是一種平台無關的二進位制格式,可以在任何支援Wasm的環境中執行,無需重新編譯。
- 高效能:Wasm旨在提供接近原生的執行速度,使其成為需要高效能的網頁應用程式的理想選擇。
- 安全性:Wasm在沙箱環境中執行,提供了強大的安全隔離,確保了網頁應用程式的安全性。
// 示例:一個簡單的Rust函式,將兩個數字相加
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(5, 7);
println!("The result is: {}", result);
}
內容解密:
上述範例展示了一個簡單的Rust函式add,它接受兩個i32型別的引數並傳回它們的和。這個函式可以被編譯成Wasm二進位制檔案,並在網頁應用程式中使用。
fn add(a: i32, b: i32) -> i32:定義了一個名為add的函式,它接受兩個i32型別的引數a和b,並傳回一個i32型別的結果。a + b:計算a和b的和,並將結果傳回。let result = add(5, 7);:呼叫add函式,將5和7作為引數傳遞,並將結果儲存在變數result中。println!:列印結果到控制檯。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Rust WebAssembly 登入應用程式開發
package "Rust 記憶體管理" {
package "所有權系統" {
component [Owner] as owner
component [Borrower &T] as borrow
component [Mutable &mut T] as mutborrow
}
package "生命週期" {
component [Lifetime 'a] as lifetime
component [Static 'static] as static_lt
}
package "智慧指標" {
component [Box<T>] as box
component [Rc<T>] as rc
component [Arc<T>] as arc
component [RefCell<T>] as refcell
}
}
package "記憶體區域" {
component [Stack] as stack
component [Heap] as heap
}
owner --> borrow : 不可變借用
owner --> mutborrow : 可變借用
owner --> lifetime : 生命週期標註
box --> heap : 堆積分配
rc --> heap : 引用計數
arc --> heap : 原子引用計數
stack --> owner : 棧上分配
note right of owner
每個值只有一個所有者
所有者離開作用域時值被釋放
end note
@enduml圖表翻譯: 此圖示展示了從編譯Rust程式碼到在網頁應用程式中執行Wasm模組的流程。首先,Rust程式碼被編譯成Wasm二進位制檔案。然後,這個二進位制檔案被納入網頁應用程式中,並在瀏覽器中執行。最後,Wasm模組完成其任務,流程結束。
透過本章的學習,您將能夠掌握如何在網頁瀏覽器中執行Rust程式碼,利用Wasm的高效能和安全性優勢,為您的網頁應用程式帶來新的可能性。
瞭解WebAssembly(Wasm)
Wasm是一種底層的位元組碼,可以在虛擬機器上執行,讓開發者能夠直接在網頁瀏覽器中執行用Rust、C和C++等語言編寫的程式碼。它的主要目標是為網頁帶來高效能的應用程式,實作超越JavaScript所能達到的效率。本章節將探討Wasm在網頁開發領域中的角色,概述其主要功能和應用案例。
Rust與WebAssembly
Rust因其對記憶體安全和零成本抽象的關注,特別適合用於Wasm開發。讓我們透過以下範例來理解為什麼Rust是Wasm開發的一個有吸引力的選擇,探討諸如所有權和借用等有助於建立高效和安全的Wasm模組的特性。
Rust程式碼範例:所有權系統
fn main() {
let data = String::from("Hello, WebAssembly!");
process_data(data);
// 'data'的所有權已經轉移給process_data
// 'data'在此處不再可存取
}
fn process_data(data: String) {
// 處理data
println!("{}", data);
}
上述範例展示了Rust的所有權系統,其中data字串的所有權被轉移給process_data函式。這種所有權模型與Wasm的記憶體管理需求相吻合,提供了一種安全和高效的資料處理方式。
設定開發環境
在深入Wasm開發之前,適當地設定開發環境至關重要。本文將指導您完成從安裝Rust和組態Wasm工具鏈到設定開發工具以實作順暢工作流程的步驟。
安裝Rust
要開始使用Rust進行Wasm開發,第一步是使用rustup(官方Rust工具鏈安裝程式)安裝Rust程式語言。如果尚未安裝rustup,可以使用以下命令安裝:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
接著,將Rust新增到系統PATH,以確保Rust編譯器(rustc)和套件管理器(cargo)可從命令列存取。
source $HOME/.cargo/env
檢查Rust安裝版本:
rustc --version
cargo --version
您應該會看到類別似於圖19.1的輸出。
設定WebAssembly工具鏈
安裝Rust後,下一步是設定Wasm工具鏈,特別是使用wasm-pack。wasm-pack簡化了Rust和Wasm的工作流程,處理諸如構建、測試和發布Wasm crate等任務。安裝wasm-pack:
cargo install wasm-pack
或者,可以使用以下命令:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
檢查wasm-pack安裝版本:
wasm-pack --version
安裝後,wasm-pack成為管理Rust中Wasm專案的寶貴工具,簡化了開發工作流程。
建立Hello-Web應用程式
準備好環境後,讓我們建立一個簡單的hello-web專案。稱之為hello-wasm:
- 使用cargo-generate,我們可以根據預定義範本建立新的Rust專案:
cargo generate --git https://github.com/rustwasm/wasm-pack-template
執行上述命令將提示您輸入所需的專案名稱,如圖19.2所示。 2. 指定專案名稱為hello-wasm,該工具將根據預定義範本生成專案及其相關檔案,如下圖所示:
圖表翻譯:此圖示顯示了使用cargo-generate建立新專案的流程
- 進入hello-wasm專案後,其結構如圖19.4所示。
- 在Cargo.toml檔案中,您將找到對cargo(Rust的套件管理器和構建工具)至關重要的依賴項和元資料規範。檢查Cargo.toml檔案的內容時,您將觀察到它內在地包含了一個wasm-bindgen依賴項,如下所示:
[dependencies]
wasm-bindgen = "0.2.63"
- 在src/lib.rs檔案中,wasm-bindgen被用來建立與JavaScript的通訊。此檔案匯入了window.alert JavaScript函式並匯出了greet Rust函式。greet函式,如下面的程式碼片段所示,觸發了一個帶有歡迎訊息的警示:
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, hello-wasm!");
}
程式碼解密:
#[wasm_bindgen]屬性用於指示wasm-bindgen生成必要的膠水程式碼,以便在JavaScript和Rust之間進行函式呼叫。extern "C"區塊宣告了一個外部函式介面,用於與JavaScript進行互動。alert函式是一個JavaScript函式,用於顯示一個包含指定訊息的對話方塊。greet函式是一個Rust函式,它呼叫alert函式來顯示歡迎訊息。
- 接著,我們使用wasm-pack build命令編譯hello-wasm專案,如下圖所示:
圖表翻譯:此圖示顯示了使用wasm-pack build編譯hello-wasm專案的流程
使用Rust和WebAssembly建立使用者登入Web應用程式
本章節將介紹如何使用Rust和WebAssembly技術建立一個簡單的使用者登入Web應用程式。首先,我們將使用wasm-pack-template建立一個新的Rust專案。
建立wasm-login專案
- 使用
cargo generate指令建立新的Rust專案:
cargo generate --git https://github.com/rustwasm/wasm-pack-template
- 指定專案名稱為
wasm-login並進入該目錄:
cd wasm-login
- 使用
wasm-pack build指令編譯專案:
wasm-pack build
- 初始化新的npm專案:
npm init wasm-app www
- 進入
www目錄:
cd www
設定專案相依性
- 在
www/package.json檔案中,將devDependencies更新為:
"devDependencies": {
"wasm-login": "file:../pkg",
...
}
- 更新
www/index.js檔案,將hello-wasm-pack替換為wasm-login:
import * as wasm from "wasm-login";
- 安裝專案相依性:
npm install
- 啟動本地伺服器:
npm run start
建立登入介面
- 修改
www/index.html檔案,建立登入表單:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login Application</title>
</head>
<body>
<h2 id="login_heading">Login Page</h2>
<div class="login_container" id="login_container">
<form id="login_form" method="get" action="login.php">
<label id="username_label">
<b>User Name</b>
</label>
<input type="text" name="username" id="username">
<br><br>
<label id="password_label">
<b>Password</b>
</label>
<input type="password" name="password" id="password">
<br><br>
<input type="button" name="login_button" id="login_button" value="Login">
<input type="button" name="reset_button" id="reset_button" value="Reset">
<br>
</form>
</div>
<script src="./bootstrap.js"></script>
</body>
</html>
內容解密:
上述HTML程式碼建立了一個簡單的登入表單,包含使用者名稱和密碼欄位,以及登入和重設按鈕。表單的action屬性設定為login.php,但目前尚未實作後端邏輯。
顯示登入介面
- 瀏覽
http://localhost:8080/,將顯示登入介面和一個提示視窗。 - 移除
www/index.js檔案中的wasm.greet()函式呼叫,以避擴音示視窗出現在UI上方。
建立 WebAssembly 登入應用程式的完整
在前面的章節中,我們已經完成了基本的 wasm-login 應用程式的建置。現在,我們將進一步美化我們的登入介面並新增互動功能。
美化登入介面
在移除預設的警告視窗後,我們的介面看起來有些粗糙。為了提升使用者經驗,我們需要對介面進行美化。首先,我們建立一個名為 www/style.css 的 CSS 檔案,並加入以下樣式:
.div_login {
width: 382px;
overflow: hidden;
margin: auto;
margin-top: 20px;
margin-right: 0;
margin-bottom: 0;
margin-left: 450px;
padding: 80px;
background-color: #e4ebe9;
border-radius: 15px;
}
h2 {
text-align: center;
padding: 20px;
}
#Uname,
#Pass {
width: 300px;
height: 30px;
border-radius: 3px;
padding-left: 8px;
}
#btn_login {
float: left;
}
#btn_reset {
float: right;
}
內容解密:
.div_login類別用於設定登入表單的樣式,包括寬度、邊距、背景顏色和圓角。h2標籤用於設定標題的樣式,使其置中並增加內距。#Uname和#Pass是輸入欄位的 ID,它們的樣式被設定為相同的寬度和高度,以保持一致性。#btn_login和#btn_reset是登入和重設按鈕的 ID,它們分別被設定為浮動在左邊和右邊。
接著,我們需要在 www/index.html 檔案中參照這個 CSS 檔案:
<head>
<meta charset="utf-8">
<title>Login Application</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
內容解密:
- 在 HTML 檔案的
<head>部分,我們新增了一個<link>標籤來參照剛才建立的style.css檔案。
完成上述步驟後,我們的登入介面應該會呈現出更好的外觀。
新增互動功能
目前我們的登入和重設按鈕尚未實作任何功能。接下來,我們將為這些按鈕新增點選事件。
首先,我們需要在 www/index.js 檔案中定義兩個函式:click_login 和 click_reset,並將它們與對應的按鈕點選事件繫結:
document.getElementById('btn_login').addEventListener('click', ev => click_login());
document.getElementById('btn_reset').addEventListener('click', ev => click_reset());
function click_login() {
let uname = document.getElementById("Uname").value;
let pwd = document.getElementById("Pass").value;
if (wasm.login(uname, pwd)) {
// 登入成功
} else {
wasm.login_failure();
}
}
function click_reset() {
document.getElementById("Uname").value = "";
document.getElementById("Pass").value = "";
}
內容解密:
click_login函式會取得使用者輸入的使用者名稱和密碼,並呼叫wasm.login函式進行驗證。- 如果驗證成功,則執行相關操作;否則,呼叫
wasm.login_failure函式顯示錯誤訊息。 click_reset函式會重設使用者名稱和密碼輸入欄位的值。
在 Rust 的 wasm-login/src/lib.rs 檔案中,我們定義了 login 函式:
#[wasm_bindgen]
pub fn login(uname: &str, passwd: &str) -> bool {
if uname == "abhishek" && passwd == "rust" {
return true;
} else {
return false;
}
}
內容解密:
login函式接受使用者名稱和密碼作為引數,並與預設的值進行比較。- 如果匹配成功,則傳回
true;否則,傳回false。
完成上述步驟後,我們需要重新編譯專案並啟動伺服器:
wasm-pack build
npm install
npm run start
開啟瀏覽器並導航到 http://localhost:8080/,即可看到我們的新登入介面。
測試登入功能
輸入錯誤的使用者名稱或密碼並點選「Log In」按鈕,會觸發一個警告視窗顯示錯誤訊息。
輸入正確的使用者名稱 abhishek 和密碼 rust,並點選「Log In」按鈕,會看到一個歡迎訊息,且使用者名稱和密碼輸入欄位會消失。
點選「Log Out」按鈕,會回到原始的登入介面。