返回文章列表

Yew 框架構建高效能 Web 應用

本文探討如何使用 Yew 框架構建高效能 Web 應用程式。Yew 根據 Rust 和 WebAssembly,提供類別似 React 的元件化開發體驗,並示範如何構建一個 Catdex 應用程式,包含檔案上傳、刪除和 CSS 樣式設計等功能。文章涵蓋 Yew 元件的基礎知識、狀態管理、事件處理、檔案處理及 CSS

Web 開發 前端開發

Yew 是一個根據 Rust 和 WebAssembly 的前端框架,提供類別似 React 的元件化開發模式,能有效提升 Web 應用程式的效能。本文將以建構 Catdex 應用程式為例,逐步說明如何使用 Yew 框架進行開發,包含圖片上傳、刪除及樣式設計等功能。我們將會探討 Yew 元件的生命週期、狀態管理、事件處理、檔案處理以及如何整合 CSS 樣式,讓讀者能快速掌握 Yew 框架的開發技巧,並應用於實際專案中。

使用 Yew 框架建立高效能 Web 前端應用程式

Yew 是目前最受歡迎的 WebAssembly 框架之一,其設計深受 React 和 Elm 的影響。瞭解這些框架有助於更好地掌握 Virtual DOM 和回應式架構的概念。

建立一個簡單的 Hello World 專案

首先,使用 Cargo 建立一個新的 Rust 專案,並新增 Yew 作為依賴。

$ cargo new hello-yew-world
$ cd hello-yew-world
$ cargo add yew --features csr

接著,更新 Cargo.toml 檔案以符合以下設定:

[package]
name = "yew-app"
version = "0.1.0"
edition = "2021"

[dependencies]
yew = { version = "0.20.0", features = ["csr"] }

內容解密:

  • cargo new hello-yew-world:建立一個新的 Rust 專案。
  • cargo add yew --features csr:新增 Yew 作為依賴,並啟用 csr 功能。
  • 更新 Cargo.toml 檔案:確保依賴版本正確。

編寫 Yew 應用程式

更新 main.rs 檔案如下:

use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    html! { <Button value=0/> }
}

#[derive(Properties, PartialEq)]
struct ButtonProp {
    value: i64,
}

#[function_component(Button)]
fn increment_button(button: &ButtonProp) -> Html {
    let counter = use_state(|| button.value);
    let on_click = {
        let counter = counter.clone();
        move |_| {
            let new_value = *counter + 1;
            counter.set(new_value);
        }
    };
    html! {
        <div>
            <button onclick={on_click}>{ "+1" }</button>
            <p>{*counter}</p>
        </div>
    }
}

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

內容解密:

  • #[function_component(App)]:定義一個名為 App 的函式元件。
  • html! { <Button value=0/> }:使用 html! 巨集建立 HTML 節點。
  • #[derive(Properties, PartialEq)]:為 ButtonProp 結構體匯出 PropertiesPartialEq 特性。
  • use_state:建立一個狀態鉤子(hook),用於儲存元件狀態。
  • on_click:定義按鈕點選事件的處理函式。

建立 index.html 檔案

在專案根目錄下建立 index.html 檔案,內容如下:

<!DOCTYPE html>
<html lang="en">
<head></head>
<body></body>
</html>

內容解密:

  • 建立一個基本的 HTML 檔案結構。

建置和執行專案

安裝 Trunk 工具,並使用它來建置和執行專案。

$ cargo install trunk
$ trunk serve --open

內容解密:

  • cargo install trunk:安裝 Trunk 工具。
  • trunk serve --open:建置並執行專案,同時開啟瀏覽器存取 http://127.0.0.1:8080

Yew 元件的原理

Yew 的元件是透過函式定義的,傳回 Html 型別。元件的屬性透過結構體表示,並使用 Properties 特性進行標註。狀態管理透過 use_state 鉤子實作,當狀態更新時,Yew 會自動重新渲染相關元件。

此圖示說明瞭 Yew 元件的生命週期和狀態管理流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Yew 框架構建高效能 Web 應用

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

內容解密:

  • 圖表展示了 Yew 元件的建立、屬性和狀態定義、事件處理以及重新渲染的流程。

使用 Yew 框架建立高效能 Web 前端應用程式

深入理解 Yew 的基礎元件與狀態管理

在前面的章節中,我們已經建立了一個簡單的計數器應用程式,展示了 Yew 的基本用法。現在,我們將進一步探討如何使用 Yew 建立一個更複雜的應用程式——一個貓咪索引(Catdex)。

貓咪索引(Catdex)應用程式的需求

我們的 Catdex 應用程式需要具備以下功能:

  • 顯示貓咪列表
  • 上傳新的貓咪資訊
  • 刪除特定的貓咪資訊

這些功能將幫助我們瞭解如何在 Yew 中渲染列表、管理狀態以及在元件之間傳遞資料。

建立 Catdex 應用程式的基本結構

首先,讓我們建立一個新的 Cargo 專案並新增 Yew 依賴:

$ cargo new catdex-yew
$ cd catdex-yew
$ cargo add yew --features csr

接下來,我們需要建立一個 index.html 檔案,並在 src/main.rs 中定義我們的 App 元件:

use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let cat_list = use_state(|| Vec::<CatDetails>::new());
    let on_change = {
        let cat_list = cat_list.clone();
        // 稍後實作 on_change 回呼函式
    };

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

#[derive(Clone)]
struct CatDetails {
    name: String,
    image: Vec<u8>,
}

fn cat(file: &CatDetails) -> Html {
    // 稍後實作 cat 函式
}

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

實作 cat 函式以顯示貓咪資訊

要顯示貓咪資訊,我們需要在 cat 函式中使用 base64 套件將圖片資料編碼為 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>
    }
}

上傳檔案並儲存貓咪資訊

要上傳檔案並儲存貓咪資訊,我們需要使用 js-sysgloo-filewasm-bindgen-futuresweb-sys 等套件。以下是一個範例程式碼,用於讀取上傳的檔案並更新 cat_list 狀態:

let on_change = {
    let cat_list = cat_list.clone();
    Callback::from(move |e: Event| {
        let input: FileInput = e.target_unchecked_into();
        if let Some(file) = input.files().and_then(|files| files.get(0)) {
            let file_reader = FileReader::new().unwrap();
            let file_reader_clone = file_reader.clone();
            let cat_list_clone = cat_list.clone();

            file_reader.onloadend = Some(move |_| {
                let result = file_reader_clone.result().unwrap();
                let bytes = result.dyn_into::<js_sys::Uint8Array>().unwrap().to_vec();
                // 更新 cat_list 狀態
                cat_list_clone.set(vec![CatDetails {
                    name: "新貓咪".to_string(),
                    image: bytes,
                }]);
            });

            file_reader.read_as_array_buffer(&file).unwrap();
        }
    })
};

#### 內容解密:

在上述程式碼中,我們使用了 FileReader API 將上傳的檔案讀取為陣列緩衝區(Array Buffer),然後將其轉換為 Vec<u8> 以儲存貓咪的圖片資料。同時,我們更新了 cat_list 狀態以包含新的貓咪資訊。

高效能網頁前端開發:使用WebAssembly的實務解析

以Yew框架實作Catdex應用程式

本章節將探討如何利用Rust語言與Yew框架結合WebAssembly技術,開發高效能的網頁前端應用程式。我們將以一個名為Catdex的貓咪圖鑑應用程式為例,逐步介紹其開發過程與關鍵技術。

檔案上傳功能的實作

首先,我們需要實作檔案上傳功能,讓使用者可以上傳貓咪圖片至Catdex應用程式中。以下程式碼展示瞭如何使用gloo_filewasm_bindgen_futures等crate來處理檔案上傳:

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

// 定義on_change事件處理函式
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());
            let mut interior_cat_list = cat_list.deref().clone();
            for file in files {
                let new_details = CatDetails {
                    name: file.name(),
                    image: gloo_file::futures::read_as_bytes(&file).await.unwrap(),
                };
                interior_cat_list.push(new_details)
            }
            cat_list.set(interior_cat_list);
        })
    }
};

#### 內容解密:
1. **事件處理**: `on_change` 事件處理函式被定義為一個閉包,負責處理檔案上傳事件。
2. **檔案處理**: `upload_file` 函式負責將 `FileList` 轉換為 `Vec<File>`,以便於在Rust中處理
3. **非同步處理**: 使用 `spawn_local` 來非同步處理檔案上傳,避免阻塞主執行緒。
4. **狀態更新**: 將新上傳的貓咪資料加入到 `cat_list` 中,並更新狀態。

#### 上傳檔案的輔助函式

```rust
fn upload_file(files: Option<FileList>) -> Vec<File> {
    files
        .map(|files| {
            js_sys::try_iter(&files)
                .unwrap()
                .unwrap()
                .map(|v| web_sys::File::from(v.unwrap()))
                .map(File::from)
                .collect()
        })
        .unwrap_or_default()
}

#### 內容解密:
1. **檔案列表處理**: `upload_file` 函式處理 `FileList` 並將其轉換為 `Vec<File>`。
2. **錯誤處理**: 使用 `unwrap`  `unwrap_or_default` 來處理可能的錯誤情況。
3. **檔案轉換**:  `web_sys::File` 轉換為 `gloo_file::File` 以便於在Rust中使用

### CSS樣式設計

為了提升Catdex應用程式的視覺效果,我們需要加入CSS樣式Yew框架本身不包含官方的CSS支援,但可以透過第三方crate如`stylist`來實作。

#### 使用stylist crate進行樣式設計

首先,新增`stylist` crate至專案中

```bash
$ cargo add stylist --features yew
$ cargo add stylist --features parser

接著,在App元件中使用styled_component巨集來載入CSS樣式:

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. **樣式載入**: 使用 `include_str!` 巨集載入 `index.css` 檔案內容。
2. **樣式套用**: 將載入的CSS樣式套用到頂層 `div` 元素上。
3. **元件樣式化**: 使用 `styled_component` 巨集來標記 `App` 元件,使其支援樣式化。

### 刪除檔案功能實作

為了讓使用者可以刪除已上傳的貓咪圖片,我們需要在`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`。
2. **狀態更新**: 根據提供的貓咪名稱,過濾掉對應的貓咪資料並更新 `cat_list` 狀態。
3. **回呼函式傳遞**:  `delete_cat` 回呼函式傳遞給 `cat` 元件,以便在需要時觸發刪除操作。

透過本章節的介紹,我們成功地使用Rust與Yew框架結合WebAssembly技術,實作了一個具備檔案上傳、CSS樣式設計與刪除檔案功能的高效能網頁前端應用程式Catdex。這不僅展示了WebAssembly在網頁前端開發中的強大潛力,也為開發者提供了一個實用的範例與參考。