乐闻世界logo
搜索文章和话题

Yew 中如何处理事件,支持哪些类型的事件?

2月19日 16:24

Yew 中的事件处理机制

Yew 提供了强大的事件处理机制,类似于 React,但使用 Rust 的类型系统来确保类型安全。

基本事件处理

1. 点击事件

rust
#[function_component(ClickHandler)] fn click_handler() -> Html { let onclick = Callback::from(|_| { web_sys::console::log_1(&"Button clicked!".into()); }); html! { <button onclick={onclick}> { "Click Me" } </button> } }

2. 输入事件

rust
#[function_component(InputHandler)] fn input_handler() -> Html { let value = use_state(|| String::new()); let oninput = { let value = value.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); value.set(input.value()); }) }; html! { <div> <input type="text" value={(*value).clone()} oninput={oninput} placeholder="Type something..." /> <p>{ "You typed: " }{ &*value }</p> </div> } }

3. 表单提交事件

rust
#[function_component(FormHandler)] fn form_handler() -> Html { let username = use_state(|| String::new()); let password = use_state(|| String::new()); let onsubmit = { let username = username.clone(); let password = password.clone(); Callback::from(move |e: SubmitEvent| { e.prevent_default(); web_sys::console::log_2( &"Username:".into(), &(*username).clone().into() ); }) }; let onusername_input = { let username = username.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); username.set(input.value()); }) }; let onpassword_input = { let password = password.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); password.set(input.value()); }) }; html! { <form onsubmit={onsubmit}> <div> <label>{ "Username:" }</label> <input type="text" value={(*username).clone()} oninput={onusername_input} /> </div> <div> <label>{ "Password:" }</label> <input type="password" value={(*password).clone()} oninput={onpassword_input} /> </div> <button type="submit">{ "Submit" }</button> </form> } }

事件类型

Yew 支持多种事件类型,每种类型都有对应的 Rust 类型:

事件类型Rust 类型常见用途
onclickMouseEvent鼠标点击
ondblclickMouseEvent鼠标双击
onmouseoverMouseEvent鼠标悬停
onmouseoutMouseEvent鼠标移出
oninputInputEvent输入变化
onchangeEvent值改变
onsubmitSubmitEvent表单提交
onkeydownKeyboardEvent键盘按下
onkeyupKeyboardEvent键盘抬起
onfocusFocusEvent获得焦点
onblurFocusEvent失去焦点

高级事件处理

1. 事件冒泡和捕获

rust
#[function_component(EventBubbling)] fn event_bubbling() -> Html { let parent_click = Callback::from(|e: MouseEvent| { web_sys::console::log_1(&"Parent clicked".into()); e.stop_propagation(); }); let child_click = Callback::from(|_| { web_sys::console::log_1(&"Child clicked".into()); }); html! { <div onclick={parent_click} style="padding: 20px; background: lightblue;"> { "Parent" } <div onclick={child_click} style="padding: 10px; background: lightgreen;"> { "Child" } </div> </div> } }

2. 自定义事件

rust
#[function_component(CustomEvent)] fn custom_event() -> Html { let on_custom = Callback::from(|data: String| { web_sys::console::log_1(&format!("Custom event received: {}", data).into()); }); html! { <ChildComponent on_custom={on_custom} /> } } #[derive(Properties, PartialEq)] pub struct ChildProps { pub on_custom: Callback<String>, } #[function_component(ChildComponent)] fn child_component(props: &ChildProps) -> Html { let trigger_custom = { let on_custom = props.on_custom.clone(); Callback::from(move |_| { on_custom.emit("Custom data from child".to_string()); }) }; html! { <button onclick={trigger_custom}> { "Trigger Custom Event" } </button> } }

3. 防抖和节流

rust
use std::rc::Rc; use std::cell::RefCell; use std::time::Duration; #[function_component(DebouncedInput)] fn debounced_input() -> Html { let value = use_state(|| String::new()); let debounced_value = use_state(|| String::new()); let timeout_handle = use_mut_ref(|| None::<gloo_timers::callback::Timeout>); let oninput = { let value = value.clone(); let debounced_value = debounced_value.clone(); let timeout_handle = timeout_handle.clone(); Callback::from(move |e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); let new_value = input.value(); value.set(new_value.clone()); // 清除之前的定时器 if let Some(handle) = timeout_handle.borrow_mut().take() { handle.cancel(); } // 设置新的防抖定时器 let debounced_value = debounced_value.clone(); let timeout = gloo_timers::callback::Timeout::new(300, move || { debounced_value.set(new_value); }); *timeout_handle.borrow_mut() = Some(timeout); }) }; html! { <div> <input type="text" value={(*value).clone()} oninput={oninput} placeholder="Type with debounce..." /> <p>{ "Debounced value: " }{ &*debounced_value }</p> </div> } }

事件处理最佳实践

1. 使用 Callback::from 简化代码

rust
// 简单事件处理 let onclick = Callback::from(|_| { web_sys::console::log_1(&"Clicked".into()); }); // 带状态的事件处理 let onclick = { let count = count.clone(); Callback::from(move |_| count.set(*count + 1)) };

2. 避免不必要的克隆

rust
// 不好的做法:每次渲染都克隆 let onclick = Callback::from({ let value = value.clone(); move |_| { // 使用 value } }); // 好的做法:使用 Rc 或引用 let onclick = Callback::from({ let value = Rc::clone(&value); move |_| { // 使用 value } });

3. 类型安全的事件处理

rust
// 使用具体的事件类型 let oninput = Callback::from(|e: InputEvent| { let input: HtmlInputElement = e.target_unchecked_into(); let value = input.value(); // 处理输入值 }); // 而不是使用通用的事件类型 let oninput = Callback::from(|e: Event| { let target = e.target().unwrap(); let input: HtmlInputElement = target.unchecked_into(); // 处理输入值 });

常见问题

1. 如何阻止默认行为?

rust
let onsubmit = Callback::from(|e: SubmitEvent| { e.prevent_default(); // 处理表单提交 });

2. 如何阻止事件冒泡?

rust
let onclick = Callback::from(|e: MouseEvent| { e.stop_propagation(); // 处理点击 });

3. 如何获取事件目标?

rust
let onclick = Callback::from(|e: MouseEvent| { let target = e.target().unwrap(); let element: HtmlElement = target.unchecked_into(); // 使用元素 });

性能优化

1. 使用 use_callback 避免重复创建

rust
#[function_component(OptimizedHandler)] fn optimized_handler() -> Html { let count = use_state(|| 0); let onclick = use_callback(count.clone(), |count, _| { count.set(*count + 1); }); html! { <button onclick={onclick}> { "Click me: " }{ *count } </button> } }

2. 事件委托

rust
#[function_component(EventDelegation)] fn event_delegation() -> Html { let items = use_state(|| vec!["Item 1", "Item 2", "Item 3"]); let onclick = Callback::from(|e: MouseEvent| { let target = e.target().unwrap(); if let Some(element) = target.dyn_ref::<HtmlElement>() { if let Some(text) = element.text_content() { web_sys::console::log_1(&format!("Clicked: {}", text).into()); } } }); html! { <div onclick={onclick}> { items.iter().map(|item| { html! { <div key={item.to_string()}> { item } </div> } }).collect::<Html>() } </div> } }
标签:Yew