uni-app 页面跳转后定时器仍在运行,因页面仅触发 onHide 而未销毁,需在 onHide 和 onUnload 中均清除定时器并置 null,且跳转决策应基于时间戳校验而非定时器驱动。

uni-app 页面跳转后定时器还在跑,是因为没清干净
页面用 uni.navigateTo 跳走,原页面实例没销毁,只是触发了 onHide;setInterval 或 setTimeout 依然在后台执行。这不是 bug,是 JS 运行时机制和小程序页面栈设计共同导致的——定时器不随视图隐藏而自动停。
必须在 onUnload 和 onHide 里都 clearTimeout / clearInterval
只靠 beforeDestroy 或 onUnload 不够:微信/支付宝小程序中,跳转到 tabBar 页面会触发 onHide 而不触发 onUnload;APP 端切后台也可能只走 onHide。漏掉任一入口,定时器就继续跑。
-
onHide:页面不可见时(跳转、切后台)立即清理,防止轮询类请求持续发出 -
onUnload:页面真正销毁时再清一次,兜底防重复注册或残留 - 清除后务必把定时器变量设为
null,避免后续误判或重复清除失败
示例:
mounted
- 正确做法:子组件通过
ref暴露控制方法(如stopTimer()),由父页面在onHide中调用 - 不要把定时器 ID 存在子组件
data里再传给父组件去清——异步时机难控,容易清早或清晚 - 更稳妥的是:子组件自身也响应
onHide,但需通过@dcloudio/uni-app显式导入(Vue 3 项目里不能直接写)
倒计时跳转类场景,别信“每秒减一”的 setInterval
用 setInterval 做倒计时 + 跳转,90% 会出问题:小程序后台冻结 JS,切回来时 count 还卡在 2,但实际已超时 3 分钟。跳转逻辑必须基于时间戳比对,而非本地计数。
- 服务端下发绝对截止时间
expireAt(毫秒时间戳) - 前端只用
Date.now() >= this.expireAt判断是否该跳转 - UI 层倒计时展示可用
requestAnimationFrame,每次重算剩余毫秒,避免 setData 频繁 - 跳转前必须检查并关闭所有遮罩:
uni.hideLoading()、this.$refs.popup?.hide?.(),否则新页面可能黑屏
关键点在于:定时器不是用来驱动业务逻辑的,只是驱动 UI 更新;真实跳转决策永远来自时间戳校验。











