组件绑定
2026/5/18大约 2 分钟euvuirustwasmusage-introductionbinding
Props Down / Callback Up
父组件通过 props 传递数据给子组件,子组件通过回调函数通知父组件:
// 父组件
html! {
div {
input {
r#type: "text"
value: parent_message.get()
oninput: on_input_value(parent_message)
}
child_display {
message: parent_message.get()
onclick: move |_event: NativeEvent| {
child_response.set(format!("Child received: {}", parent_message.get()));
}
}
}
}
// 子组件 — 提取 props 和回调
pub fn child_display(props: VirtualNode) -> VirtualNode {
let message: String = props.try_get_prop("message").unwrap_or_default();
let onclick: Option<NativeEventHandler> = props.try_get_event("onclick");
html! {
div {
p { "Child received: " message }
button {
onclick: onclick
"Respond to Parent"
}
}
}
}强类型 Props
通过 try_get_typed_prop 传递非 String 类型(如 bool、i32)的 props:
// 父组件传递 bool 和 i32
html! {
limited_counter {
disabled: is_disabled
max_count: max_count
on_increment: my_handler
on_reset: my_reset_handler
}
}
// 子组件提取强类型 props
pub fn limited_counter(props: VirtualNode) -> VirtualNode {
let disabled: bool = props.try_get_typed_prop("disabled").unwrap_or(false);
let max_count: i32 = props.try_get_typed_prop("max_count").unwrap_or(10);
let on_increment: Option<NativeEventHandler> = props.try_get_callback("on_increment");
let on_reset: Option<NativeEventHandler> = props.try_get_callback("on_reset");
html! {
div {
p { "Disabled: " {disabled.to_string()} ", Max: " {max_count.to_string()} }
button {
onclick: on_increment
disabled: disabled
"+1"
}
}
}
}提示
try_get_typed_prop 通过 FromStr 自动将属性字符串解析为目标类型。布尔值 true/false 和数字 42 等都会被正确解析。
自定义回调
通过自定义属性名传递回调函数,子组件用 try_get_callback 提取:
// 父组件传递自定义回调
html! {
callback_input {
on_change: my_on_change_handler
on_submit: my_on_submit_handler
on_reset: my_on_reset_handler
}
}
// 子组件提取回调
pub fn callback_input(props: VirtualNode) -> VirtualNode {
let on_change: Option<NativeEventHandler> = props.try_get_callback("on_change");
let on_submit: Option<NativeEventHandler> = props.try_get_callback("on_submit");
let on_reset: Option<NativeEventHandler> = props.try_get_callback("on_reset");
html! {
div {
input {
r#type: "text"
oninput: on_change
}
button {
onclick: on_submit
"Submit"
}
button {
onclick: on_reset
"Reset"
}
}
}
}提示
try_get_callback 与 try_get_event 功能相同,只是语义上用于自定义回调名而非标准 DOM 事件名。
双向绑定(Shared Signal)
父子组件共享同一个 Signal,任一方修改立即同步到另一方:
let shared_text: Signal<String> = use_signal(|| "Type here...".to_string());
let shared_count: Signal<i32> = use_signal(|| 0);
// 父组件
html! {
div {
p { "Text: " shared_text }
p { "Count: " shared_count }
button {
onclick: move |_event: NativeEvent| {
let current: i32 = shared_count.get();
shared_count.set(current + 1);
}
"+"
}
}
// 将 Signal 直接传给子组件
{ child_input(shared_text, shared_count) }
}
// 子组件 — 接收 Signal 参数(非 props)
pub fn child_input(text_signal: Signal<String>, count_signal: Signal<i32>) -> VirtualNode {
html! {
div {
input {
r#type: "text"
value: text_signal.get()
oninput: on_input_value(text_signal)
}
button {
onclick: move |_event: NativeEvent| {
let current: i32 = count_signal.get();
count_signal.set(current - 1);
}
"-"
}
}
}
}提示
Signal 实现了 Copy,传递给子组件的是同一个信号的副本,指向相同的内部状态。不需要通过 props 传递。
跨组件响应式绑定(watch!)
使用 watch! 宏连接不同信号,一个信号变化时自动更新另一个:
let celsius: Signal<f64> = use_signal(|| 0.0);
let fahrenheit: Signal<f64> = use_signal(|| 32.0);
// 摄氏度变化 → 更新华氏度
watch!(celsius, |celsius_value| {
fahrenheit.set(celsius_value * 9.0 / 5.0 + 32.0);
});
// 华氏度变化 → 更新摄氏度
watch!(fahrenheit, |fahrenheit_value| {
celsius.set((fahrenheit_value - 32.0) * 5.0 / 9.0);
});多信号联动(RGB → Hex 颜色):
let red: Signal<i32> = use_signal(|| 79);
let green: Signal<i32> = use_signal(|| 70);
let blue: Signal<i32> = use_signal(|| 229);
let hex_color: Signal<String> = use_signal(|| "#4f46e5".to_string());
watch!(red, green, blue, |red_value, green_value, blue_value| {
hex_color.set(format!(
"#{:02x}{:02x}{:02x}",
red_value.clamp(0, 255),
green_value.clamp(0, 255),
blue_value.clamp(0, 255)
));
});