组件的一生只有六个常用时刻:setup → onBeforeMount → onMounted → onBeforeUpdate → onUpdated → onBeforeUnmount → onUnmounted。
搞清「哪一步 DOM 才存在」,就知道取 DOM 引用、起定时器、清理现场各该写在哪。① 和 ② 用的是同一个 <life-ticker> 子组件。
| 钩子 | 此刻能干什么 |
|---|---|
setup() | 声明响应式数据、注册其它钩子。DOM 还不存在,模板 ref 是 null,也别在这里发请求读写界面 |
onBeforeMount | render 即将开始、真实 DOM 还没生成。基本只用来打日志/埋点 |
onMounted | 主力:拿模板 ref、focus、起定时器、接图表库、发首屏请求 |
onBeforeUpdate | 数据已变、DOM 还是旧的 —— 想在改动前记录滚动位置等,写这里 |
onUpdated | DOM 已跟上新数据,需要同步第三方视图时才用;别在这里再改数据 |
onBeforeUnmount | DOM 还在、还能读,卸载前最后的抢救点 |
onUnmounted | 清理专用:clearInterval / removeEventListener / 取消订阅 / abort 请求 |
| 误用 | 后果 / 正解 |
|---|---|
在 setup 里 document.querySelector / 读模板 ref | 拿到 null。挪到 onMounted |
在 onUpdated 里改响应式数据 | 更新 → 钩子 → 再更新,死循环(Vue 会报 Maximum recursive updates) |
| 定时器/监听只起不清 | 组件卸载后回调还在跑,闭包里的数据永远回收不掉 → 内存泄漏。写在 onUnmounted |
| 改完数据立刻读 DOM | 读到旧值,DOM 是异步更新的 → 用 nextTick |
| 想「通过 ref 调子组件私有状态」 | 拿到的是实例,能不能访问由子组件的 expose 决定 |