mcpField
L
leptos-guide

leptos-guide

github.com/daiki48开发0110 次安装2 天前更新
代码仓库

关于 leptos-guide

面向 Leptos v0.8.x 开发者的开发指南,涵盖响应式应用构建。重点说明信号、异步数据、组件和所有权处理模式。

主要功能

  1. 01信号与响应式原语模式
  2. 02组件、Effect 与 Memo
  3. 03异步资源与表单 Action
  4. 04上下文与列表渲染
  5. 05所有权与闭包处理指导

适用场景

  1. 01在 Leptos 中实现响应式状态
  2. 02在视图中加载异步数据
  3. 03处理表单提交和事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/daiki48/dotfiles --skill leptos-guide

安装后在 Claude Code 中输入/leptos-guide确认已加载。

详细介绍

Leptos v0.8.x 开发指南

信号(响应式原语)

let (count, set_count) = signal(0);   // Tuple form
let count = RwSignal::new(0);         // RwSignal form

// Read
count.get()    // Clone value
count.read()   // Get reference (&T)

// Write
set_count.set(1)
set_count.update(|n| *n += 1)
count.write()  // Get &mut T

组件

#[component]
pub fn MyComponent(
    title: String,
    #[prop(optional)] class: Option<String>,
    #[prop(default = 10)] limit: usize,
    children: Children,
) -> impl IntoView {
    view! {
        <div class=class.unwrap_or_default()>
            <h1>{title}</h1>
            {children()}
        </div>
    }
}

Effect 和 Memo

// Side effects
Effect::new(move |_| { log::info!("count: {}", count.get()); });

// Derived signal (cached)
let doubled = Memo::new(move |_| count.get() * 2);

Resource(异步数据)

let user = Resource::new(
    move || user_id.get(),
    |id| async move { fetch_user(id).await }
);

view! {
    <Suspense fallback=|| view! { <p>"Loading..."</p> }>
        {move || user.get().map(|u| view! { <p>{u.name}</p> })}
    </Suspense>
}

Action(表单提交)

let submit = Action::new(|data: &FormData| {
    let data = data.clone();
    async move { submit_form(data).await }
});

view! {
    <form on:submit=move |ev| {
        ev.prevent_default();
        submit.dispatch(form_data);
    }> /* ... */ </form>
}

模式

条件渲染
<Show when=move || count.get() > 0 fallback=|| view! { <p>"Empty"</p> }>
    <p>"Has items"</p>
</Show>

{move || match status.get() {
    Status::Loading => view! { <p>"Loading"</p> }.into_any(),
    Status::Ok(data) => view! { <Data data/> }.into_any(),
}}
列表渲染
<For
    each=move || items.get()
    key=|item| item.id
    children=|item| view! { <li>{item.name}</li> }
/>
上下文
// Provider
provide_context(AppState::new());

// Consumer
let state = expect_context::<AppState>();

所有权模式(重要)

为什么需要

Leptos 信号没有实现 Copy。将其移入闭包或异步块会转移所有权。

StoredValue(推荐用于异步操作)
let api = StoredValue::new(client.clone());

Effect::new(move |_| {
    let api = api.get_value();  // Get inside Effect
    spawn_local(async move { api.fetch().await; });
});
Callback(事件处理器)
let on_delete = Callback::new(move |id: i32| {
    set_items.update(|items| items.retain(|i| i.id != id));
});

// Child: on_delete.run(id)
克隆模式
let count = RwSignal::new(0);
let count_clone = count.clone();
let closure1 = move || count.get();
let closure2 = move || count_clone.get();

常见错误

// ❌ Wrong: get_value outside Effect
let api_value = api.get_value();
Effect::new(move |_| { spawn_local(async move { api_value.fetch().await; }); });

// ✅ Correct: get_value inside Effect
Effect::new(move |_| {
    let api = api.get_value();
    spawn_local(async move { api.fetch().await; });
});

// ❌ Wrong: Infinite loop
Effect::new(move |_| { signal.set(signal.get() + 1); });

// ✅ Correct: Use get_untracked()
Effect::new(move |_| {
    let val = signal.get_untracked();
    if should_update { signal.set(val + 1); }
});

快速参考

用例模式
Effect 中的 API 客户端StoredValue::new(client.clone())
多个闭包中的信号let sig_clone = sig.clone()
子级→父级事件Callback<T>
跟踪前一个值StoredValue::new(initial)
条件读取signal.get_untracked()

评论

支持 Markdown