返回文章列表

Yew WebAssembly 建構高效能網頁前端

本文介紹如何使用 Rust 的 Yew 框架和 WebAssembly 技術構建高效能網頁前端應用程式,以貓咪圖鑑為例,涵蓋專案初始設定、元件結構、圖片顯示、檔案上傳處理、CSS 樣式設計,以及刪除功能的實作。同時也探討了 Yew 框架的替代方案,以及如何使用 Rust 建構 REST API 後端。

Web 開發 前端開發

Yew 是一個根據 Rust 和 WebAssembly 的前端框架,能建構高效能的網頁應用程式。本文以建構貓咪圖鑑為例,逐步講解如何使用 Yew 開發前端應用。首先,我們需要設定專案結構,包含使用 use_state hook 管理貓咪列表,並透過檔案輸入元件上傳圖片。接著,我們會實作 cat 函式,將貓咪資訊轉換為 HTML 並顯示,其中包含使用 base64 編碼顯示圖片。為了處理檔案上傳,我們會利用 gloo_filewasm_bindgen_futures 進行非同步操作。最後,我們使用 stylist crate 來管理 CSS 樣式,並實作刪除貓咪圖片的功能,提升使用者經驗。

使用 Yew 和 WebAssembly 建立高效能網頁前端:貓咪圖鑑範例

本章節將介紹如何使用 Rust 語言的 Yew 框架和 WebAssembly 技術來建立一個高效能的網頁前端應用程式。我們將以建立一個貓咪圖鑑(Catdex)為例,逐步引導讀者完成整個開發過程。

專案初始設定與基本結構

首先,我們需要建立一個基本的 Yew 應用程式結構。這包括定義一個 App 元件函式,使用 use_state 這個 hook 來儲存目前的貓咪列表(CatDetails),並提供一個檔案輸入元件來上傳圖片。

use yew::prelude::*;

struct CatDetails {
    name: String,
    image: Vec<u8>,
}

fn app() -> Html {
    let cat_list = use_state(|| vec![]);
    let on_change = {
        let cat_list = cat_list.clone();
        move |e: Event| {
            // 處理檔案上傳事件
        }
    };

    html! {
        <div>
            <h1>{"Catdex"}</h1>
            <input type="file" accept="image/*" onchange={on_change} />
            <section class="cats">
                { for cat_list.iter().map(cat) }
            </section>
        </div>
    }
}

fn cat(file: &CatDetails) -> Html {
    // 將 CatDetails 轉換為 HTML
}

fn main() {
    yew::Renderer::<App>::new().render();
}

內容解密:

  1. use_state hook 用於儲存和管理貓咪列表的狀態。
  2. on_change 事件處理器負責處理檔案上傳事件,並更新貓咪列表。
  3. html! 巨集用於定義元件的 HTML 結構。

貓咪資訊的顯示

接下來,我們需要實作 cat 函式,將 CatDetails 轉換為 HTML,並顯示貓咪的名稱和圖片。為了顯示圖片,我們需要將圖片資料進行 base64 編碼。

use base64::{engine::general_purpose, Engine};

fn cat(cat: &CatDetails) -> Html {
    html! {
        <article class="cat">
            <h3>{ format!( "{}", cat.name )}</h3>
            <img src={
                format!("data:image;base64,{}",
                    general_purpose::STANDARD.encode(&cat.image))
            } />
        </article>
    }
}

內容解密:

  1. 使用 base64 crate 將圖片資料進行 base64 編碼。
  2. 將編碼後的圖片資料嵌入到 img 標籤的 src 屬性中。

檔案上傳處理

為了處理檔案上傳,我們需要實作 on_change 事件處理器。這個處理器會讀取上傳的檔案,並將其轉換為 CatDetails 物件,然後更新貓咪列表。

use gloo_file::File;
use wasm_bindgen_futures::spawn_local;
use web_sys::{FileList, HtmlInputElement};

let on_change = {
    let cat_list = cat_list.clone();
    move |e: Event| {
        let cat_list = cat_list.clone();
        spawn_local(async move {
            let input: HtmlInputElement = e.target_unchecked_into();
            let files = upload_file(input.files());
            // 處理上傳的檔案
        })
    }
};

內容解密:

  1. 使用 gloo_filewasm_bindgen_futures crate 來處理檔案上傳。
  2. 將上傳的檔案轉換為 CatDetails 物件,並更新貓咪列表。

CSS 樣式設計

最後,我們需要為我們的應用程式新增 CSS 樣式,以改善其視覺效果。我們可以使用 stylist crate 來整合 CSS 樣式到我們的 Yew 元件中。

use stylist::{yew::styled_component, Style};

#[styled_component(App)]
fn app() -> Html {
    const CSS: &str = include_str!("index.css");
    let stylesheet = Style::new(CSS).unwrap();

    html! {
        <div class={stylesheet}>
            // 元件內容
        </div>
    }
}

內容解密:

  1. 使用 stylist crate 來整合 CSS 樣式到 Yew 元件中。
  2. 將 CSS 樣式套用到元件的根元素上。

在 Rust 中使用 Yew 框架建立高效能 Web 前端

替換元件樣式

在我們的 Rust 程式碼中,我們已將 function_component 替換為 styled_component。接著,我們使用 (include_str!) 巨集從 src 資料夾載入 index.css,並將此字串轉換為樣式表。最後,為頂層的 div 提供此樣式表作為類別屬性。這些相對簡單的步驟使任何元件都能擁有自己的樣式,並且父元件能夠以正常層疊方式將樣式套用到子元件。你可以在圖 4-8 中看到這些新增樣式表的結果。

CSS 程式碼範例

.cats {
    display: flex;
}

.cat {
    border: 1px solid grey;
    min-width: 200px;
    min-height: 350px;
    margin: 5px;
    padding: 5px;
    text-align: center;
}

.cat > img {
    width: 190px;
}

內容解密:

  1. .cats 類別使用 display: flex; 將貓的列表以彈性盒子方式顯示,使其能夠自動調整排列。
  2. .cat 類別為每個貓的專案設定邊框、最小寬度、最小高度、邊距和內距,使其在頁面上呈現良好的間距和對齊。
  3. .cat > img 選擇器針對 .cat 內的圖片設定寬度,確保圖片在設定的範圍內正確顯示。

刪除檔案功能實作

目前的頁面設計存在一個明顯問題:可以新增貓到頁面,但無法刪除已新增的貓。是時候來修復這個問題了。

首先,我們需要在頂層的 App 元件中建立一個回呼函式,當提供檔案名稱時,該函式將從貓列表中移除該貓。

Rust 程式碼範例

let delete_cat = {
    let cat_list = cat_list.clone();
    Callback::from(move |name: String| {
        let interior_cat_list = cat_list.deref().clone();
        let new_cat_list: Vec<_> = interior_cat_list
            .into_iter()
            .filter(|cat| cat.name != name)
            .collect();
        cat_list.set(new_cat_list);
    })
};

內容解密:

  1. 建立一個名為 delete_cat 的回呼函式,並複製 cat_list 以在閉包中使用。
  2. 當回呼函式被觸發時,它會根據提供的名稱過濾出需要保留的貓,並更新 cat_list 狀態。
  3. 這樣,當使用者點選刪除按鈕時,可以正確地從列表中移除指定的貓。

新增刪除按鈕元件

接下來,你需要一個按鈕元件來觸發這個回呼函式。

Rust 程式碼範例

#[derive(Properties, PartialEq)]
struct ButtonProp {
    text: String,
    name: String,
    on_click: Callback<String>
}

#[function_component(Button)]
fn delete_button(button: &ButtonProp) -> Html {
    let on_click = {
        let name = button.name.clone();
        let callback = button.on_click.clone();
        move |_| {
            callback.emit(name.clone())
        }
    };
    html! {
        <div>
            <button onclick={on_click}>
                { button.text.clone() }
            </button>
        </div>
    }
}

內容解密:

  1. 定義了一個名為 ButtonProp 的結構體,用於傳遞按鈕的屬性,如文字、名稱和點選回呼函式。
  2. 建立了一個名為 Button 的功能元件,用於渲染一個按鈕。
  3. 當按鈕被點選時,會觸發傳入的回呼函式,並傳遞按鈕相關的名稱。

修改 Cat 元件以支援刪除功能

fn cat(cat: &CatDetails, callback: Callback<String>) -> Html {
    html! {
        <article class="cat">
            <h3>{ format!( "{}", cat.name )}</h3>
            <Button text={"Delete".to_string()}
                    name={cat.name.clone()}
                    on_click={callback}
            />
            <img src={format!("data:image;base64,{}",
                              general_purpose::STANDARD.encode(&cat.image))} />
        </article>
    }
}

內容解密:

  1. 修改了 cat 元件,使其接受一個回呼函式,並將其傳遞給 Button 元件。
  2. 當點選刪除按鈕時,會觸發回呼函式,從而刪除對應的貓。

WebAssembly 替代方案

WebAssembly 是一個多功能的平台,因此有許多不同的工具和框架專注於不同的主題。本章介紹的工具大多由 Rust 和 WebAssembly 工作組維護。

其他前端框架

  • Darco:受 Elm 和 Redux 啟發。
  • Percy:支援同構 Web 應用程式,即相同的程式碼在伺服器端和客戶端執行。
  • Seed:受 Elm、React 和 Redux 啟發。
  • Smithy:另一個根據 Rust 的 WebAssembly 前端框架。

這些框架大多受到其他語言中流行的前端框架和模式的啟發,為開發者提供了多樣化的選擇。

使用 Rust 建構高效能的 WebAssembly 前端與 REST API 後端

WebAssembly 與 Rust 的結合

WebAssembly(Wasm)是一種二進位制指令格式,允許開發者在瀏覽器中執行高效能的程式碼。Rust 語言因其安全性和效能優勢,成為了開發 Wasm 模組的理想選擇。本章節將介紹如何使用 Rust 編譯至 Wasm,並在 Web 前端中使用。

使用 Rust 開發 WebAssembly 模組

Rust 提供了多種工具和函式庫來簡化 Wasm 模組的開發流程。以下是一個簡單的範例,展示如何使用 Rust 編譯至 Wasm,並在瀏覽器中執行:

use wasm_bindgen::prelude::*;

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

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

內容解密:

此範例程式碼使用了 wasm_bindgen 函式庫來產生與 JavaScript 的介面。#[wasm_bindgen] 屬性用於標記需要匯出至 JavaScript 的函式。greet 函式接受一個字串引數,並呼叫瀏覽器的 alert 函式來顯示問候訊息。

在前端中使用 WebAssembly

將 Rust 編譯至 Wasm 後,可以在前端專案中使用。以下是一個簡單的範例,展示如何使用 JavaScript 來載入和執行 Wasm 模組:

import * as wasm from './your_wasm_module';

wasm.greet('World');

內容解密:

此範例程式碼展示瞭如何使用 JavaScript 來匯入和執行 Wasm 模組。greet 函式是由 Rust 編譯至 Wasm 的函式,可以在 JavaScript 中直接呼叫。

使用 Yew 框架開發前端應用

Yew 是一個使用 Rust 和 Wasm 開發前端應用的框架。以下是一個簡單的範例,展示如何使用 Yew 來建立一個可以載入、顯示和刪除貓圖片的前端應用:

use yew::prelude::*;

enum Msg {
    AddCat,
    RemoveCat,
}

struct App {
    cats: Vec<String>,
}

impl Component for App {
    type Message = Msg;
    type Properties = ();

    fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
        Self { cats: vec![] }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::AddCat => {
                self.cats.push("new cat".to_string());
                true
            }
            Msg::RemoveCat => {
                self.cats.pop();
                true
            }
        }
    }

    fn change(&mut self, _: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            <div>
                <button onclick=self.link.callback(|_| Msg::AddCat)>{ "Add Cat" }</button>
                <button onclick=self.link.callback(|_| Msg::RemoveCat)>{ "Remove Cat" }</button>
                <ul>
                    { for self.cats.iter().map(|cat| html! { <li>{ cat }</li> }) }
                </ul>
            </div>
        }
    }
}

內容解密:

此範例程式碼展示瞭如何使用 Yew 框架來建立一個前端應用。App 結構體代表了應用的狀態,update 方法處理了訊息,view 方法產生了 HTML。

建構 REST API 後端

Rust 也可用於建構高效能的 REST API 後端。以下是一個簡單的範例,展示如何使用 Rust 和 actix-web 框架來建立一個 REST API:

use actix_web::{web, App, HttpResponse, HttpServer, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok().body("Hello, World!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

內容解密:

此範例程式碼展示瞭如何使用 actix-web 框架來建立一個 REST API。index 函式處理了根路徑的 GET 請求,main 函式啟動了 HTTP 伺服器。