关于 leptos-guide
面向 Leptos v0.8.x 开发者的开发指南,涵盖响应式应用构建。重点说明信号、异步数据、组件和所有权处理模式。
主要功能
- 01信号与响应式原语模式
- 02组件、Effect 与 Memo
- 03异步资源与表单 Action
- 04上下文与列表渲染
- 05所有权与闭包处理指导
适用场景
- 01在 Leptos 中实现响应式状态
- 02在视图中加载异步数据
- 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