//! 每实例独立的定时器所有权 composable。 //! //! 背景:模块级/组件级 let timer 单例共享(如 useToast 原模块级 _timer)会跨实例串扰 —— //! 组件 B 的 showToast/onUnmounted 清掉组件 A 的 timer,致 A 的 toast 永不隐藏; //! 组件卸载后 timer 仍可能触发(向单例 store 写入,setState-after-unmount)。 //! //! 分工:本 composable 为每个调用实例持有独立的 timeout/interval 注册表, //! onUnmounted 自动全清,互不影响。setOwnTimeout 触发后自动从注册表移除(一次性)。 //! 适用:toast 自动隐藏 / keyword 防抖 / FilePreview 等后续 debounce 场景。 //! //! 注意:仅在组件 setup 内调用(内部依赖 onUnmounted 生命周期钩子)。 import { onUnmounted } from 'vue' export function useTimerOwnership() { const timeouts = new Set>() const intervals = new Set>() /** 注册一次性 timeout;触发后自动从注册表移除。返回可交给 clearOwn 的 id。 */ function setOwnTimeout(fn: () => void, ms: number) { const id = setTimeout(() => { timeouts.delete(id) fn() }, ms) timeouts.add(id) return id } /** 注册重复 interval;onUnmounted 自动清理。返回可交给 clearOwnInterval 的 id。 */ function setOwnInterval(fn: () => void, ms: number) { const id = setInterval(fn, ms) intervals.add(id) return id } /** 清理指定 timeout(id 为 null/undefined 时 no-op)。 */ function clearOwn(id?: ReturnType | null) { if (id != null) { clearTimeout(id) timeouts.delete(id) } } /** 清理指定 interval(id 为 null/undefined 时 no-op)。 */ function clearOwnInterval(id?: ReturnType | null) { if (id != null) { clearInterval(id) intervals.delete(id) } } onUnmounted(() => { timeouts.forEach(clearTimeout) intervals.forEach(clearInterval) timeouts.clear() intervals.clear() }) return { setOwnTimeout, setOwnInterval, clearOwn, clearOwnInterval } }