euv 中的数据获取与 API 集成
现代 Web 应用很少孤立存在。它们需要与服务器通信、从 API 获取数据,并将用户输入发送回后端服务。在 euv 中,数据获取和 API 集成通过框架的异步操作支持、响应式信号系统和事件驱动架构来处理。
本文提供了 euv 中数据获取和 API 集成的全面指南,涵盖了从基本 HTTP 请求到处理加载状态、错误和缓存的高级模式。
基础:euv 中的异步操作
euv 内置了对异步操作的支持,这对于 API 调用至关重要。该框架将 Rust 的 async/await 语法与其响应式信号系统相集成,允许你编写感觉自然并与 UI 无缝集成的异步代码。
当你需要从 API 获取数据时,通常会创建一个执行 HTTP 请求并返回结果的异步函数。然后可以将结果存储在信号中,让 UI 对其做出反应。
基本数据获取模式
euv 中最常见的数据获取模式涉及三个信号:一个用于数据,一个用于加载状态,一个用于可能发生的任何错误。以下是这个模式的工作原理:
let data: Signal<String> = use_signal(|| "".to_string());
let loading: Signal<bool> = use_signal(|| false);
let error: Signal<String> = use_signal(|| "".to_string());
当用户触发数据获取时——例如,通过点击按钮——你更新加载状态,执行异步操作,然后根据结果更新数据或错误信号:
button {
onclick: move |_event: Event| {
loading.set(true);
error.set("".to_string());
// 异步数据获取会在这里发生
// 结果会存储在 data 信号中
}
"Fetch Data"
}
这种模式让你可以完全控制加载和错误状态,你可以用它们向用户提供 UI 反馈。
使用事件处理程序进行 API 调用
在 euv 中,用户交互通过 onclick、oninput 和 onchange 等事件处理程序来处理。这些处理程序是在响应用户操作时触发 API 调用的自然位置。
例如,你可能希望在提交表单时获取用户数据:
let username: Signal<String> = use_signal(|| "".to_string());
let errors: Signal<String> = use_signal(|| "".to_string());
button {
onclick: move |_event: Event| {
let mut validation_errors: Vec<String> = Vec::new();
if username.get().trim().is_empty() {
validation_errors.push("Username is required".to_string());
}
if validation_errors.is_empty() {
errors.set("".to_string());
// 在这里触发 API 调用
submitted.set(format!("Submitted: {}", username.get()));
} else {
errors.set(validation_errors.join("; "));
}
}
"Submit"
}
在这个示例中,onclick 处理程序首先验证表单,如果验证通过,就会触发 API 调用来提交数据。submitted 信号存储成功提交的结果。
使用计算信号进行响应式数据显示
从 API 获取数据后,你需要在 UI 中显示它。euv 的响应式信号系统使这变得简单直接。你可以使用 html! 宏来渲染数据,每当底层信号发生变化时,UI 会自动更新。
对于显示获取的数据,你可以使用条件渲染来处理不同的状态:
html! {
div {
if loading.get() {
span { "Loading..." }
} else if !error.get().is_empty() {
span { class: "error", error }
} else {
span { data }
}
}
}
这种模式确保用户始终看到数据获取的当前状态——无论是加载中、出错还是已成功完成。
用于响应式数据获取的 watch! 宏
watch! 宏可用于在特定状态发生变化时自动获取数据。这对于像即时搜索这样的场景很有用——每当用户输入新查询时,你希望获取结果:
watch!(search_query, |query: String| {
if !query.trim().is_empty() {
// 使用新查询触发 API 调用
loading.set(true);
// 获取数据并更新信号
}
});
这个 watcher 会在 search_query 信号每次变化时触发,允许你自动获取新数据,无需用户点击按钮。
带有 API 集成的表单提交
Web 应用中常见的模式是通过表单收集用户输入,然后将其发送到 API。在 euv 中,这涉及将表单处理与异步操作相结合:
let username: Signal<String> = use_signal(|| "".to_string());
let email: Signal<String> = use_signal(|| "".to_string());
let errors: Signal<String> = use_signal(|| "".to_string());
let submitted: Signal<String> = use_signal(|| "".to_string());
html! {
div {
input {
r#type: "text"
placeholder: "Enter username"
value: username
oninput: move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
username.updater.set(input.value());
}
}
}
button {
onclick: move |_event: Event| {
let mut validation_errors: Vec<String> = Vec::new();
if username.get().trim().is_empty() {
validation_errors.push("Username is required".to_string());
}
if email.get().trim().is_empty() {
validation_errors.push("Email is required".to_string());
}
if validation_errors.is_empty() {
errors.set("".to_string());
submitted.set(format!("Submitted: {}", username.get()));
} else {
errors.set(validation_errors.join("; "));
}
}
"Submit"
}
}
}
在这种模式中,表单通过信号收集用户输入,在提交时对其进行验证,然后通常会触发异步 API 调用将数据发送到服务器。
处理加载状态
加载状态对于在 API 调用期间提供良好的用户体验至关重要。在 euv 中,你可以使用布尔信号来跟踪 API 调用是否正在进行:
let loading: Signal<bool> = use_signal(|| false);
html! {
div {
if loading.get() {
span { "Loading..." }
} else {
// 显示获取的数据
span { data }
}
}
}
你还可以禁用加载时的交互元素以防止重复请求:
button {
disabled: loading
onclick: move |_event: Event| {
loading.set(true);
// 执行 API 调用
}
if loading.get() { "Loading..." } else { "Fetch Data" }
}
错误处理
健壮的错误处理对于任何与 API 通信的应用都至关重要。在 euv 中,你可以使用专门的错误信号来存储和显示错误消息:
let error: Signal<String> = use_signal(|| "".to_string());
// 当 API 调用失败时:
error.set("Failed to fetch data. Please try again.".to_string());
// 当错误解决时:
error.set("".to_string());
你可以将错误处理与条件渲染结合使用,在 UI 中显示错误消息:
html! {
div {
if !error.get().is_empty() {
span { class: "error-message", error }
}
}
}
使用 html! 宏进行数据驱动渲染
euv 中的 html! 宏支持条件渲染和列表渲染,这对于显示从 API 获取的数据至关重要。你可以使用 if/else 块来处理不同状态,使用 for 循环来渲染数据列表:
html! {
div {
if data.get().is_empty() {
span { "No data available" }
} else {
// 渲染数据
span { data }
}
}
}
对于更复杂的数据结构,你可以使用 html! 宏中的 for 循环语法来迭代集合并渲染每个项目。
组件级数据获取
当构建需要获取自己数据的组件时,你可以将数据获取逻辑封装在组件内部。#[component] 宏使这变得干净且可复用:
#[derive(Clone, Default)]
struct MyCardProps {
title: &'static str,
onclick: Option<Rc<dyn Fn(Event)>>,
disabled: Signal<bool>
}
#[component]
pub fn my_card(node: VirtualNode<MyCardProps>) -> VirtualNode {
let MyCardProps { title, .. } = node.try_get_props().unwrap_or_default();
let children: VirtualNode = node.try_get_child_node();
html! {
div {
h3 { title }
children
}
}
}
虽然这是一个通用组件示例,但你可以创建专用的数据获取组件,接受 API 端点作为属性,并管理自己的加载和错误状态。
总结
euv 中的数据获取和 API 集成遵循围绕框架响应式信号系统构建的一致模式:
- 使用信号存储状态:将数据、加载状态和错误存储在信号中。
- 从事件处理程序触发获取:使用
onclick和其他事件处理程序发起 API 调用。 - 使用
watch!进行响应式获取:在状态变化时自动获取数据。 - 处理加载和错误状态:在 API 调用期间向用户提供反馈。
- 使用条件渲染:根据当前状态显示不同的 UI。
- 封装在组件中:创建可复用的数据获取组件。
通过遵循这些模式,你可以构建有效与 API 通信的 euv 应用,同时提供流畅且响应迅速的用户体验。Rust 的异步能力与 euv 响应式信号系统的结合使数据获取既强大又优雅。