You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yew漫画阅读器:切换路由时如何中止Pending GET请求?

Yew漫画阅读器:路由切换时中止未完成的缩略图请求

我正在开发一款基于Yew的漫画阅读器,首页是带缩略图的书架,点击缩略图可进入书籍内容页。现在碰到个问题:缩略图还没加载完就点进内容页,页面得等所有pending的缩略图请求都完成才开始加载书籍内容。想在首页组件销毁(切换到内容页)时中止所有未完成的缩略图请求,让内容页立刻开始加载。


解决方案一:手动用AbortController中止请求

Yew的Request支持通过AbortController中止请求,利用组件use_effect的清理函数就能实现组件卸载时自动中止请求。

给Book组件添加请求中止逻辑

修改Book组件,用AbortController管理缩略图请求,组件卸载时自动触发中止:

#[function_component]
fn Book(props: BookProps) -> Html {
    let thumbnail = use_state(|| None::<String>);
    let abort_controller = use_state(|| AbortController::new());

    {
        let thumbnail = thumbnail.clone();
        let abort_controller = abort_controller.clone();
        let book_name = props.name.clone();
        use_effect_with((), move |_| {
            let thumbnail = thumbnail.clone();
            let abort_controller = abort_controller.clone();
            wasm_bindgen_futures::spawn_local(async move {
                let url = format!("{}/books/{}/thumbnail", BACKEND_URL, book_name);
                let request = Request::get(&url)
                    .signal(abort_controller.signal()) // 绑定中止信号
                    .body(())
                    .unwrap();
                
                match Request::send(&request).await {
                    Ok(response) => {
                        if response.ok() {
                            let img_url = response.text().await.unwrap();
                            thumbnail.set(Some(img_url));
                        }
                    },
                    Err(_) => {
                        // 请求被中止,无需额外处理
                    }
                }
            });

            // 组件卸载时执行清理函数,中止请求
            move || {
                abort_controller.abort();
            }
        });
    }

    // 点击跳转逻辑
    let navigate = use_navigate();
    let onclick = {
        let navigate = navigate.clone();
        let book_name = props.name.clone();
        Callback::from(move |_| {
            navigate.to(&Route::BookContent { name: book_name.clone() });
        })
    };

    html! {
        <div onclick={onclick}>
            {
                if let Some(img) = *thumbnail {
                    <img src={img} alt={&props.name} />
                } else {
                    <div>{ "加载中..." }</div>
                }
            }
        </div>
    }
}

可选:在BookShelf层面统一管理请求

如果想在书架组件销毁时一次性中止所有子组件的请求,可以用Context传递AbortController:

  1. 定义Context:
#[derive(Clone, PartialEq)]
struct RequestAbortContext {
    abort_controller: AbortController,
}

static REQUEST_ABORT_CONTEXT: Context<RequestAbortContext> = Context::new();
  1. 在BookShelf中创建并提供Context:
#[styled_component]
fn BookShelf() -> Html {
    let books = use_state(|| vec![]);
    let abort_controller = use_state(|| AbortController::new());

    // 获取书籍列表的原有逻辑...

    // 书架组件卸载时中止所有请求
    use_effect_with((), move |_| {
        let abort_controller = abort_controller.clone();
        move || {
            abort_controller.abort();
        }
    });

    html! {
        <REQUEST_ABORT_CONTEXT.Provider value={RequestAbortContext { abort_controller: (*abort_controller).clone() }}>
            {/* 原有书籍列表渲染逻辑 */}
        </REQUEST_ABORT_CONTEXT.Provider>
    }
}
  1. 在Book组件中使用Context:
#[function_component]
fn Book(props: BookProps) -> Html {
    let ctx = use_context::<RequestAbortContext>().unwrap();
    // 后续请求绑定ctx.abort_controller.signal()即可
}

解决方案二:用Yew Query替代手动请求管理

Yew Query是Yew生态里类似React Query的库,内置请求中止、缓存等功能,完全适配组件生命周期,和你提到的Theo视频思路一致。

1. 添加依赖

在Cargo.toml中添加:

yew-query = "0.2"

2. 用use_query获取缩略图

修改Book组件,无需手动处理请求中止,组件卸载时Yew Query会自动完成:

use yew_query::prelude::*;

#[function_component]
fn Book(props: BookProps) -> Html {
    let query_client = use_query_client();
    let book_name = props.name.clone();

    // 定义查询,组件卸载时自动中止未完成请求
    let thumbnail_query = use_query(
        QueryKey::from(vec!["thumbnail", &book_name]),
        move |_| async move {
            let url = format!("{}/books/{}/thumbnail", BACKEND_URL, book_name);
            let response = Request::get(&url)
                .send()
                .await?
                .text()
                .await?;
            Ok(response)
        },
        QueryOptions::default(),
    );

    // 点击跳转逻辑和之前一致...

    html! {
        <div onclick={onclick}>
            {
                match thumbnail_query.state() {
                    QueryState::Loading => html! { <div>{ "加载中..." }</div> },
                    QueryState::Success(img) => html! { <img src={img} alt={&props.name} /> },
                    QueryState::Error(_) => html! { <div>{ "缩略图加载失败" }</div> },
                }
            }
        </div>
    }
}

关于Context + use_location/use_route的思路

这个思路是可行的:可以用Context维护当前路由状态,在Book组件里监听路由变化,当路由离开首页时中止请求。但相比前两种方案,需要手动维护路由监听和请求的关联,复杂度更高,不如直接用AbortController的清理函数或Yew Query来得简洁。


内容的提问来源于stack exchange,提问作者Austin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:46:03