Rust Web框架Leptos与Spin集成宏库leptos-spin-macro的使用,实现高效无服务器函数开发

Rust Web框架Leptos与Spin集成宏库leptos-spin-macro的使用,实现高效无服务器函数开发

安装

在项目目录中运行以下Cargo命令:

cargo add leptos-spin-macro

或者在Cargo.toml中添加以下行:

leptos-spin-macro = "0.2.0"

使用示例

leptos-spin-macro提供了宏来简化Leptos与Spin无服务器框架的集成。下面是一个完整的使用示例:

use leptos::*;
use leptos_spin_macro::server;

// 使用#[server]宏标记无服务器函数
#[server(MyServerFunction, "/api")]
pub async fn my_server_function(input: String) -> Result<String, ServerFnError> {
    // 在这里编写服务器端逻辑
    Ok(format!("Processed: {}", input))
}

// 在Leptos组件中使用
#[component]
pub fn MyComponent() -> impl IntoView {
    let action = create_server_action::<MyServerFunction>();
    
    view! {
        <div>
            <form on:submit=|ev| {ev.prevent_default(); action.dispatch(MyServerFunction {input: "Hello".to_string()})}>
                <button type="submit">Submit</button>
            </form>
            
            {move || {
                action.value().get().map(|result| match result {
                    Ok(response) => view! { <p>{response}</p> },
                    Err(e) => view! { <p>Error: {e.to_string()}</p> },
                })
            }}
        </div>
    }
}

完整示例Demo

下面是一个完整的Spin无服务器应用示例,展示了如何使用leptos-spin-macro:

// src/lib.rs
use leptos::*;
use leptos_spin_macro::server;

#[server(AddNumbers, "/api/add")]
pub async fn add_numbers(a: i32, b: i32) -> Result<i32, ServerFnError> {
    Ok(a + b)
}

#[component]
pub fn Calculator() -> impl IntoView {
    let add_action = create_server_action::<AddNumbers>();
    let result = add_action.value();
    
    view! {
        <div>
            <h1>Calculator</h1>
            <form on:submit=|ev| {
                ev.prevent_default();
                add_action.dispatch(AddNumbers { a: 5, b: 7 });
            }>
                <button type="submit">Add 5 + 7</button>
            </form>
            
            <p>
                {move || match result.get() {
                    Some(Ok(sum)) => format!("Result: {}", sum),
                    Some(Err(e)) => format!("Error: {}", e),
                    None => "Waiting...".to_string(),
                }}
            </p>
        </div>
    }
}

#[spin_sdk::http_component]
fn handle_request(req: http::Request<()>) -> Result<http::Response<()>> {
    let mut conf = leptos::get_configuration(None)?;
    conf.leptos_options.output_name = "calculator".to_string();
    
    leptos_spin::handle_http(req, conf, || view! { <Calculator/> })
}

配置

在spin.toml中配置应用:

spin_version = "1"
trigger = { type = "http", base = "/" }

[[component]]
id = "calculator"
source = "target/wasm32-wasi/release/calculator.wasm"
allowed_outbound_hosts = []
[component.trigger]
route = "/..."

构建与运行

# 构建Wasm组件
cargo build --target wasm32-wasi --release

# 运行Spin应用
spin up

这个示例展示了如何:

  1. 定义无服务器函数
  2. 在Leptos组件中使用
  3. 配置Spin应用
  4. 构建和运行无服务器应用

leptos-spin-macro简化了Leptos和Spin的集成,使开发者能够专注于业务逻辑而不是框架集成细节。


1 回复

Rust Web框架Leptos与Spin集成宏库leptos-spin-macro的使用

概述

leptos-spin-macro是一个将Leptos前端框架与Spin无服务器框架集成的Rust宏库,它简化了在Spin环境中使用Leptos构建高效无服务器函数的过程。

主要特性

  • 无缝集成Leptos和Spin框架
  • 简化无服务器函数的创建流程
  • 提供高效的响应式前端开发体验
  • 支持服务器端渲染(SSR)

使用方法

1. 添加依赖

首先在Cargo.toml中添加必要的依赖:

[dependencies]
leptos = "0.5"
leptos-spin-macro = "0.1"
spin-sdk = "2.0"

2. 基本使用示例

use leptos::*;
use leptos_spin_macro::component;

#[component]
pub fn HelloWorld() -> impl IntoView {
    view! {
        <h1>"Hello, Spin + Leptos!"</h1>
    }
}

#[spin_component]
fn handle_request() -> impl IntoView {
    view! {
        <html>
            <head>
                <title>"Leptos Spin App"</title>
            </head>
            <body>
                <HelloWorld/>
            </body>
        </html>
    }
}

3. 处理请求数据

use spin_sdk::http::{Request, Response};
use leptos_spin_macro::{spin_component, server};

#[server]
async fn get_data() -> Result<String, ServerFnError> {
    // 这里可以执行异步操作,如数据库查询等
    Ok("Data from server".to_string())
}

#[spin_component]
fn handle_request(req: Request) -> impl IntoView {
    let data = create_resource(|| (), |_| async { get_data().await });
    
    view! {
        <div>
            <h1>"Data Fetching Example"</h1>
            <Suspense fallback=|| view! { <p>"Loading..."</p> }>
                {move || data.read().map(|data| 
                    match data {
                        Ok(data) => view! { <p>{data}</p> },
                        Err(e) => view! { <p>"Error loading data"</p> }
                    }
                )}
            </Suspense>
        </div>
    }
}

4. 路由处理

use leptos::*;
use leptos_router::*;
use leptos_spin_macro::spin_component;

#[spin_component]
fn handle_request(req: Request) -> impl IntoView {
    view! {
        <Router>
            <Routes>
                <Route path="/" view=HomePage/>
                <Route path="/about" view=AboutPage/>
            </Routes>
        </Router>
    }
}

#[component]
fn HomePage() -> impl IntoView {
    view! { <h1>"Welcome to the Home Page"</h1> }
}

#[component]
fn AboutPage() -> impl IntoView {
    view! { <h1>"About Us"</h1> }
}

高级用法

1. 自定义响应头

use spin_sdk::http::{Request, Response};
use leptos_spin_macro::spin_component;

#[spin_component]
fn handle_request(req: Request) -> impl IntoView {
    let headers = vec![
        ("Content-Type".to_string(), "text/html".to_string()),
        ("Custom-Header".to_string(), "Value".to_string())
    ];
    
    view! {
        headers=headers,
        <h1>"Custom Headers Example"</h1>
    }
}

2. 状态管理

use leptos::*;
use leptos_spin_macro::spin_component;

#[spin_component]
fn handle_request(req: Request) -> impl IntoView {
    let (count, set_count) = create_signal(0);
    
    view! {
        <div>
            <button on:click=move |_| set_count.update(|n| *n += 1)>
                "Click me: " {count}
            </button>
        </div>
    }
}

部署到Spin

  1. 构建WASM组件:
cargo build --target wasm32-wasi --release
  1. 创建spin.toml配置文件:
spin_version = "1"
name = "leptos-spin-app"
trigger = { type = "http", base = "/" }
version = "0.1.0"

[[component]]
source = "target/wasm32-wasi/release/your_app.wasm"
id = "leptos-app"
[component.trigger]
route = "/..."
  1. 运行应用:
spin up

注意事项

  1. 确保使用Rust nightly工具链,因为Leptos需要一些nightly特性
  2. WASM目标需要正确安装:rustup target add wasm32-wasi
  3. 对于生产部署,考虑添加适当的错误处理和日志记录

这个集成库为开发者提供了在Spin无服务器环境中使用Leptos框架的便捷方式,结合了两者的优势,使得构建高效、响应式的无服务器应用变得更加简单。

完整示例demo

以下是一个结合了路由、数据获取和状态管理的完整示例:

use leptos::*;
use leptos_router::*;
use leptos_spin_macro::{spin_component, server};
use spin_sdk::http::{Request, Response};

// 定义服务器端函数
#[server]
async fn fetch_user_data(id: u32) -> Result<String, ServerFnError> {
    // 模拟异步获取数据
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    Ok(format!("User data for ID: {}", id))
}

// 主页组件
#[component]
fn HomePage() -> impl IntoView {
    let (count, set_count) = create_signal(0);
    
    view! {
        <div>
            <h1>"Welcome to Leptos Spin App"</h1>
            <button on:click=move |_| set_count.update(|n| *n += 1)>
                "Clicked: " {count} " times"
            </button>
            <p>
                <a href="/about">"Go to About Page"</a>
            </p>
            <p>
                <a href="/user/42">"View User 42"</a>
            </p>
        </div>
    }
}

// 关于页面组件
#[component]
fn AboutPage() -> impl IntoView {
    view! {
        <div>
            <h1>"About This App"</h1>
            <p>"This is a demo app combining Leptos and Spin"</p>
            <p>
                <a href="/">"Back to Home"</a>
            </p>
        </div>
    }
}

// 用户详情页面组件
#[component]
fn UserPage(id: u32) -> impl IntoView {
    let user_data = create_resource(move || id, |id| async move {
        fetch_user_data(id).await
    });
    
    view! {
        <div>
            <h1>"User Profile"</h1>
            <p>"User ID: " {id}</p>
            <Suspense fallback=|| view! { <p>"Loading user data..."</p> }>
                {move || user_data.read().map(|data| 
                    match data {
                        Ok(data) => view! { <p>{data}</p> },
                        Err(_) => view! { <p>"Error loading data"</p> }
                    }
                )}
            </Suspense>
            <p>
                <a href="/">"Back to Home"</a>
            </p>
        </div>
    }
}

// 主处理函数
#[spin_component]
fn handle_request(req: Request) -> impl IntoView {
    // 设置自定义响应头
    let headers = vec![
        ("Content-Type".to_string(), "text/html".to_string()),
        ("X-Framework".to_string(), "Leptos+Spin".to_string())
    ];
    
    view! {
        headers=headers,
        <html>
            <head>
                <title>"Leptos Spin Demo"</title>
            </head>
            <body>
                <Router>
                    <Routes>
                        <Route path="/" view=HomePage/>
                        <Route path="/about" view=AboutPage/>
                        <Route path="/user/:id" view=|id: u32| view! {
                            <UserPage id=id/>
                        }/>
                    </Routes>
                </Router>
            </body>
        </html>
    }
}

这个完整示例展示了:

  1. 基本的路由配置
  2. 服务器端数据获取
  3. 客户端状态管理
  4. 自定义响应头设置
  5. 异步数据加载与Suspense处理
  6. 参数化路由

要运行这个示例,请按照前面提到的部署步骤进行操作。

回到顶部