应统一使用 safeNavigate 方法防护跳转:检查页面栈是否已存在目标页,存在则回退;添加状态锁防连续点击;switchTab 需节流并缓存时间戳;自定义 view 按钮须手动模拟禁用态;全局指令不可靠,需耦合生命周期校验。

uni.navigateTo 连续点击导致页面栈堆积
连续点击同一个 uni.navigateTo 跳转按钮,会在页面栈中压入多个相同页面实例。用户返回时会逐页回退,甚至卡在重复页面里出不来——这不是 UI 卡顿,是路由栈真实膨胀了。
根本原因在于:uni-app 默认不校验目标页面是否已在栈中,只要调用就 push。尤其在列表项、卡片、悬浮按钮等高频可点区域,手速快一点就很容易触发。
- 检查当前页面栈是否已存在目标页面:
uni.getCurrentPages()返回数组,遍历page.route对比目标url路径(注意去除 query 参数干扰) - 若已存在,优先用
uni.navigateBack({ delta: N })回退到它,而不是新建 - 若不存在,再执行
uni.navigateTo - 务必加状态锁:
isNavigating = true,跳转成功/失败后才设为false,防止中间再次点击
switchTab 重复点击引发白屏或卡死
在 tabBar 页面上用 uni.switchTab 切换时连续点击,部分安卓低端机和微信小程序真机会出现白屏、tab 闪烁、甚至整个 tab 区域不可交互。这是因为 switchTab 内部强制销毁并重建页面,频繁触发会打乱渲染队列。
解决方案不是禁用点击,而是「拦截 + 缓存判断」:
- 点击前先读取
uni.getStorageSync('lastTabSwitch'),记录上一次切换时间戳 - 若
Date.now() - lastTabSwitch ,直接 return,不执行任何跳转 - 执行
switchTab前写入新时间戳:uni.setStorageSync('lastTabSwitch', Date.now()) - 不要依赖
v-if或disabled控制 tab 按钮,switchTab的 target 是 tabBar 配置项,view 层锁定无效
自定义 view 按钮跳转时没有 disabled 属性
用 代替 实现样式自由时,天然丢失 disabled 行为。用户点击后无视觉反馈,极易误判“没点上”而连点多次。
必须手动模拟禁用态:
- 绑定
@click="handleJump",并在 data 中声明isJumping: false - 点击入口立即设
this.isJumping = true,同时改变 class(如加jump-disabled类控制 opacity 和 pointer-events) - 跳转成功回调(
success)或失败回调(fail)中,必须设this.isJumping = false - 不能只靠
setTimeout恢复,网络超时、用户切后台再回来都可能导致状态滞留
全局指令防重点击不适用于路由跳转
网上流传的 v-prevent-re-click 全局指令,在跳转场景下容易失效。它通常只做「n 秒内忽略后续 click」,但无法感知跳转是否真正完成——比如 navigateTo 成功后页面还没渲染完,用户又点了返回键,再点原按钮,指令已过期,照样触发第二次跳转。
真正可靠的防护必须耦合生命周期:
- 在跳转前调用
uni.preloadPage(如支持)预加载目标页,缩短白屏时间,降低连点动机 - 监听
onUnload(源页面卸载)或onShow(目标页显示),配合时间戳做二次校验 - 跨端兼容性注意:H5 端可用
history.state判断,小程序端只能依赖getCurrentPages()和本地缓存 - 最简兜底:所有跳转逻辑统一收口到一个
safeNavigate方法,内部集成栈检查、状态锁、节流和错误恢复
实际中最容易被忽略的点是:跳转后的页面如果也带可跳转按钮,且未做同样防护,就会形成链式重复跳转。每个跳转出口都要当作独立攻击面来加固,不能只守第一关。










