真正可靠的倒计时只能在原生层由uni-ad体系托管,pages/splash.vue因js环境未就绪无法稳定实现——冷启动时webview未初始化,settimeout不执行、video不渲染,倒计时必然错乱或失效。

pages/splash.vue 无法真正实现 App 启动页广告倒计时——冷启动时 JS 环境未就绪,video 不渲染、setTimeout 不执行、倒计时必然错乱或不触发。
真正能稳定运行的倒计时,只存在于「原生启动图关闭后、Vue 页面挂载前」这段间隙里。你写的 Vue 页面(哪怕是 pages/splash.vue)本质是 Webview 层内容,不是原生启动页。
为什么在 pages/splash.vue 里写倒计时会失败
常见错误现象包括:倒计时卡在 3 秒不动、点“跳过”无响应、切后台再回来直接跳转失败、iOS 黑屏闪动、Android 报 DOMException: play() failed。
- 原生启动图(
splash.png)由系统直接加载,此时 WebView 还没初始化,JS 引擎根本没启动,mounted和onLoad都不会触发 - 所谓“倒计时”,实际是原生图消失后,才开始渲染这个 Vue 页面——中间有 100–300ms 渲染延迟,
setTimeout(3000)从页面加载起算,总时长就不可控 - 用户快速点击“跳过”时,
uni.navigateTo可能和倒计时结束逻辑并发执行,造成路由栈混乱或白屏 - App 切后台时,JS 定时器被系统挂起,前台恢复后不会补时,导致倒计时跳秒甚至归零后不跳转
用 pages/splash.vue 模拟开屏页的实操要点
如果你不需要广告联盟计费、只是做品牌视频/引导页等非广告用途,可退而求其次,用 Vue 页面模拟。但必须满足以下硬性条件:
- 在
pages.json中将该页设为"list"第一项,并关闭导航栏:"navigationStyle": "custom" - 页面样式强制全屏覆盖:
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;,别依赖uni.getSystemInfo动态计算 - 倒计时必须用
setTimeout递归,而非setInterval;每次回调开头重算剩余时间:Math.max(0, Math.floor((endTime - Date.now()) / 1000)) - 跳过按钮绑定
@click="jump",且加catchtouchmove阻止穿透滚动,避免 iOS 滑动误触底层 tabbar -
uni.navigateTo前必须调用uni.hideLoading()和uni.hideToast(),否则遮罩残留导致黑屏
倒计时状态必须双重锁定
光靠 UI 上 disabled="isCounting" 不够,用户可能绕过按钮直接调用跳转函数。状态控制必须收口到逻辑层:
- 声明响应式字段:
isJumping: false,作为唯一跳转锁 - 点击“跳过”或倒计时结束时,统一走
this.handleJump()函数 - 函数开头立即判断:
if (this.isJumping) return,然后设this.isJumping = true - 跳转成功回调中(如
uni.navigateTo的success),再重置状态;失败则需手动this.isJumping = false - 在
onUnload和onHide中清理定时器 ID(如clearTimeout(this.countdownTimer)),否则页面销毁后定时器仍在后台运行
真机调试时最关键的验证点
别信模拟器,必须用真机看日志:
- iOS 上打开 HBuilderX 控制台,搜索
adSplash或fullScreenAd,看到[ad] splash ad loaded: true才算原生广告层真正就绪 - Android 真机上若出现黑屏→白屏→广告三段式,说明
pages/splash.vue渲染太慢,需在onLoad里加setTimeout(() => { this.startCountdown() }, 100)缓冲 - 切后台再切回,UI 必须立刻重算剩余时间,不能继续按旧值减——这意味着你不能存“剩余秒数”,只能存“结束时间戳”
- 所有跳转动作必须收口到原生广告关闭回调(如
splash.hide(() => { uni.navigateTo(...) })),绝不能放在页面生命周期钩子里
真正可靠的倒计时,从来不在 Vue 页面里“做”,而是在原生层交由 uni-ad 体系托管。你写的任何 JS 倒计时,都是对原生流程的妥协与模拟——精度、合规性、体验都打折扣。











