基于Rust Yew的表格Demo鼠标滚轮滚动时出现内存泄漏求助
Yew表格组件滚轮滚动内存泄漏排查与修复
问题背景
提供的Rust代码基于Yew框架实现了分页表格组件:App生成测试数据传递给TestTable,TestTable通过Table组件渲染表头与分页表体,滚轮滚动时触发页码切换,渲染当前页数据。但滚动过程中出现内存泄漏问题。
泄漏原因分析
Callback重复创建与引用滞留
Table组件的view方法中,每次渲染都会通过reform生成新的Callback闭包,原Callback的引用无法被GC回收。TestTable每次更新页码时,都会重新创建传递给Table的onwheelCallback,多重闭包捕获形成长期引用链,导致旧组件实例无法释放。
不必要的数据克隆
- 渲染表格行时,对
data[index][n]调用to_owned()进行字符串克隆,每次渲染都会生成大量重复字符串,增加内存占用。
- 渲染表格行时,对
高频事件触发过度渲染
- 滚轮滚动会触发大量事件,导致
TestTable频繁更新状态、重新渲染,虚拟DOM的diff无法及时回收旧元素关联的资源。
- 滚轮滚动会触发大量事件,导致
修复方案
1. 简化Callback传递逻辑
修改Table组件,直接使用props传入的Callback,避免重复生成闭包:
#[derive(PartialEq, Properties)] pub struct TableProps { onwheel: Callback<web_sys::WheelEvent>, children: Children, } pub struct Table; impl Component for Table { type Message = (); type Properties = TableProps; fn create(ctx: &Context<Self>) -> Self { Self {} } fn view(&self, ctx: &Context<Self>) -> Html { html! { <table onwheel={ctx.props().onwheel.clone()}> <thead> <tr> <th>{""}</th> <th>{"HeadCell1"}</th> <th>{"HeadCell2"}</th> <th>{"HeadCell3"}</th> <th>{"HeadCell4"}</th> <th>{"HeadCell5"}</th> </tr> </thead> <tbody> {for ctx.props().children.iter()} </tbody> </table> } } }
2. 复用Callback,避免重复创建
在TestTable的初始化阶段创建Callback,复用同一个实例:
pub struct TestTable { page: usize, per_page: usize, total: usize, onwheel_callback: Callback<web_sys::WheelEvent>, } impl Component for TestTable { type Message = (); type Properties = TestTableProps; fn create(ctx: &Context<Self>) -> Self { let onwheel_callback = ctx.link().callback(|_e: web_sys::WheelEvent| ()); Self { page: 0, per_page: 16, total: ctx.props().data.len(), onwheel_callback, } } fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool { self.page += 1; if self.page > self.total / self.per_page { self.page = 0; } true } fn view(&self, _ctx: &Context<Self>) -> Html { let mut rows = vec![]; for i in 0..self.per_page { let index = i + self.page * self.per_page; if index >= self.total { break; } // 直接引用数据,避免不必要克隆 let row_data = &self.data[index]; rows.push(html! { <tr> <td>{index}</td> <td>{format!("{}--{}", row_data[0], index)}</td> <td>{format!("{}--{}", row_data[1], index)}</td> <td>{format!("{}--{}", row_data[2], index)}</td> <td>{format!("{}--{}", row_data[3], index)}</td> <td>{format!("{}--{}", row_data[4], index)}</td> </tr> }); } html! { <Table onwheel={self.onwheel_callback.clone()}> {for rows} </Table> } } }
3. 添加防抖处理,限制更新频率
通过防抖减少高频滚轮事件触发的组件更新:
use std::time::Duration; use yew::callback::Callback; use yew::utils::debounce::Debounce; pub struct TestTable { page: usize, per_page: usize, total: usize, debounced_update: Debounce<Callback<()>>, } impl Component for TestTable { type Message = (); type Properties = TestTableProps; fn create(ctx: &Context<Self>) -> Self { let update_callback = ctx.link().callback(|_| ()); let debounced_update = Debounce::new(Duration::from_millis(100), update_callback); Self { page: 0, per_page: 16, total: ctx.props().data.len(), debounced_update, } } fn view(&self, ctx: &Context<Self>) -> Html { let onwheel = ctx.link().callback(|_e: web_sys::WheelEvent| { self.debounced_update.emit(()); }); // 表格行渲染逻辑不变... html! { <Table onwheel={onwheel}> {for rows} </Table> } } }
修复效果验证
- 重复创建Callback的问题被解决,事件监听器资源能被正确回收
- 数据引用优化减少了不必要的内存占用
- 防抖处理降低了组件更新频率,缓解了内存压力
内容的提问来源于stack exchange,提问作者Chaos
相关产品推荐
相关产品推荐

