列表渲染
2026/5/21大约 1 分钟euvuirustwasmusage-introductionlist
for 循环渲染
html! 宏支持 for 循环语法,用于渲染动态列表。循环体中的内容会被自动包装为响应式 DynamicNode,信号变化时自动重新渲染。
基本循环
let items: Signal<Vec<String>> = use_signal(|| {
vec!["Learn Rust".to_string(), "Build UI".to_string()]
});
html! {
ul {
for item in {items.get()} {
li { item }
}
}
}带索引的循环
html! {
ul {
for (index, item) in {items.get().iter().enumerate()} {
li {
span { index }
span { item }
}
}
}
}提示
for 循环的迭代表达式必须用花括号 {} 包裹,循环变量模式(如 item 或 (index, item))放在 in 关键字之前。
手动渲染列表
如果需要更细粒度的控制,也可以手动构建列表节点:
fn render_items(items: Signal<Vec<String>>) -> VirtualNode {
let item_list: Vec<String> = items.get();
let mut children: Vec<VirtualNode> = Vec::new();
for (index, item) in item_list.iter().enumerate() {
let item_clone: String = item.clone();
let index_clone: usize = index;
let node: VirtualNode = html! {
li {
item_clone
}
};
children.push(node);
}
VirtualNode::Fragment(children)
}添加项目
let items: Signal<Vec<String>> = use_signal(|| Vec::new());
let items_updater: Signal<Vec<String>> = items;
let new_item: Signal<String> = use_signal(|| "".to_string());
let new_item_updater: Signal<String> = new_item;
html! {
div {
input {
r#type: "text"
value: new_item
oninput: move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
new_item_updater.set(input.value());
}
}
}
button {
onclick: move |_event: Event| {
let text: String = new_item_updater.get();
if !text.trim().is_empty() {
let mut current: Vec<String> = items_updater.get();
current.push(text);
items_updater.set(current);
}
}
"Add"
}
}
}删除项目
let items_remove: Signal<Vec<String>> = items;
let index_clone: usize = index;
html! {
button {
onclick: move |_event: Event| {
let mut current: Vec<String> = items_remove.get();
if index_clone < current.len() {
current.remove(index_clone);
items_remove.set(current);
}
}
"Remove"
}
}