Yew 组件生命周期有哪些方法?各自适合做什么?
Yew 组件生命周期主要对应 Component trait 的几个方法:create 初始化,update 处理消息,changed 响应 props 变化,view 生成虚拟 DOM,rendered 处理真实 DOM 之后的副作用,destroy 清理资源。面试里只背方法名不够,关键要说清楚每个阶段适合放什么、不适合放什么。最容易出问题的是把副作用塞进 view,或者忘记在卸载时释放监听器和定时器。
追问
create 和 view 分别适合做什么?
create 在组件实例创建时调用,适合根据 props 初始化字段、准备默认状态和回调需要的数据。它不适合操作 DOM,因为这时节点还没有挂载,强行查元素通常会拿到空值。view 只应该根据当前 state 和 props 返回 Html,少量格式化可以放进去,但复杂计算最好提前放到 update 或缓存字段里。
update 返回 true 和 false 有什么区别?
update 收到消息后修改组件状态,返回 true 表示需要重新调用 view,返回 false 表示这次变化不影响 UI。比如计数器加一、接口数据写入 state 应该返回 true,日志上报或空操作可以返回 false。常见踩坑是所有分支都返回 true,小组件看不明显,列表页里会造成大量无意义 diff。
changed 什么时候触发,为什么不能滥用?
changed 在父组件传入的 props 变化时触发,适合根据新旧 props 判断子组件是否需要同步状态或重渲染。它的价值在于过滤无效更新,例如父组件重渲染但关键 id 没变,子组件可以返回 false。不要把所有 props 都复制成内部 state,否则很容易出现两份数据不同步;只有表单草稿、动画状态这类本地状态才值得复制。
为什么 DOM 操作要放在 rendered?
rendered 在真实 DOM 更新后调用,适合读取节点尺寸、聚焦输入框,或初始化依赖节点的第三方 JS 库。first_render 能区分首次渲染,图表实例、全局监听这类只应初始化一次的逻辑必须挡住重复执行。踩坑点是每次渲染都重新挂库,最后同一个 canvas 多个实例、多个事件监听一起存在,页面会越来越慢。
destroy 主要清理哪些资源?
destroy 在组件卸载时调用,适合取消定时器、释放 JS 事件监听、关闭 WebSocket、丢弃订阅句柄。Rust 会释放普通字段,但浏览器侧事件和外部订阅不一定按你想的时机断开。边界是一次性请求通常不用专门处理,长连接、轮询、全局事件和第三方库实例必须有明确退出路径。
写段代码
rustuse yew::prelude::*; pub enum Msg { Inc, Loaded(String), Noop } pub struct Counter { count: i32, text: String } impl Component for Counter { type Message = Msg; type Properties = (); fn create(_ctx: &Context<Self>) -> Self { Self { count: 0, text: String::new() } } fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool { match msg { Msg::Inc => { self.count += 1; true } Msg::Loaded(v) => { self.text = v; true } Msg::Noop => false, } } fn view(&self, ctx: &Context<Self>) -> Html { html! { <button onclick={ctx.link().callback(|_| Msg::Inc)}>{ self.count }</button> } } fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) { if first_render { /* 初始化依赖 DOM 的逻辑 */ } } fn destroy(&mut self, _ctx: &Context<Self>) { /* 清理资源 */ } }
小结
Yew 生命周期的核心是把事情放到正确阶段:初始化在 create,状态变更在 update,属性同步在 changed,展示在 view,DOM 副作用在 rendered,资源释放在 destroy。真正写项目时,update 的返回值、rendered 的重复执行和 destroy 的清理边界,比方法定义本身更容易踩坑。