微信小程序 navigateTo 栈超过多层后会失效
背景。自定义导航栏。体验版发布后。产品进行测试。发现底部导航切换多了以后会失效,所有uni.navigateTo都会失效。
- 真机测试不会报错。不会提示
解决方案:上代码
- 原理:检测栈的长度。超过了使用reLauch进行跳转便清空了navigateTo的栈空间,再重新继续使用navigateTo。也可以都是用reLauch,但体验度不好,会白屏后加载。
<template><view class="custom-tabbar"><!-- 彩色装饰条 - 按设计稿三色比例 --><view class="color-bars"><view class="color-bar bar-orange"></view><view class="color-bar bar-brown"></view><view class="color-bar bar-red"></view></view><!-- tabBar内容 --><view class="tabbar-content"><!-- 首页 --><view class="tab-item" @click="switchTab('home')" :class="{ 'tab-active': activeTab === 'home' }"><image:src="activeTab === 'home' ? '/static/img/figma/首页.png' : '/static/img/figma/首页1.png'"class="tab-icon":class="{ 'icon-active': activeTab === 'home' }"mode="aspectFit" /><text class="tab-text" :class="{ active: activeTab === 'home' }">首页</text></view><!-- 动态 --><view class="tab-item" @click="switchTab('dynamic')" :class="{ 'tab-active': activeTab === 'dynamic' }"><image:src="activeTab === 'dynamic' ? '/static/img/figma/动态.png' : '/static/img/figma/动态1.png'"class="tab-icon":class="{ 'icon-active': activeTab === 'dynamic' }"mode="aspectFit" /><text class="tab-text" :class="{ active: activeTab === 'dynamic' }">动态</text></view><!-- 项目 --><view class="tab-item" @click="switchTab('project')" :class="{ 'tab-active': activeTab === 'project' }"><image:src="activeTab === 'project' ? '/static/img/figma/项目.png' : '/static/img/figma/项目1.png'"class="tab-icon":class="{ 'icon-active': activeTab === 'project' }"mode="aspectFit" /><text class="tab-text" :class="{ active: activeTab === 'project' }">项目</text></view><!-- 个人中心 --><view class="tab-item" @click="switchTab('profile')" :class="{ 'tab-active': activeTab === 'profile' }"><image:src="activeTab === 'profile' ? '/static/img/figma/个人中心.png' : '/static/img/figma/个人中心1.png'"class="tab-icon":class="{ 'icon-active': activeTab === 'profile' }"mode="aspectFit" /><text class="tab-text" :class="{ active: activeTab === 'profile' }">个人中心</text></view></view><!-- 帮助中心(大logo,不可点击) --><view class="tab-item help-center"><image src="/static/bar/image10.png" class="help-icon" mode="aspectFit" /><text class="help-text">帮助中心</text></view></view>
</template><script>
export default {name: 'CustomTabBar',props: {// 当前激活的tabcurrentTab: {type: String,default: 'home'},// 是否在点击时自动导航。为false时仅切换高亮并抛出事件,不跳转页面navigateOnClick: {type: Boolean,default: true}},data() {return {activeTab: 'home',// 防重复点击,避免连续跳转isNavigating: false,// 页面栈软阈值,超过后改用reLaunch以清栈stackSoftLimit: 8};},watch: {currentTab: {handler(newVal) {this.activeTab = newVal;},immediate: true}},mounted() {// 组件挂载时,根据当前页面路径设置激活状态this.setActiveTabByCurrentPage();},methods: {/*** 切换tab页面* @param {string} tabName - tab名称*/switchTab(tabName) {if (this.activeTab === tabName) {return; // 已经是当前tab,无需切换}// 添加触觉反馈// #ifdef APP-PLUSplus.device.vibrate(50);// #endifthis.activeTab = tabName;// 若关闭自动导航,仅更新激活态并通知父级if (!this.navigateOnClick) {this.$emit('tabChange', tabName);return;}// 计算目标地址let targetUrl = '';switch (tabName) {case 'home':targetUrl = '/pages/index/index';break;case 'dynamic':targetUrl = '/pages/article/index';break;case 'project':targetUrl = '/pages/yzpg/index';break;case 'profile':targetUrl = '/pages/profile/center';break;default:console.warn('未知的tab名称:', tabName);}// 已在目标页则不跳转const pages = getCurrentPages();if (pages.length) {const currentRoute = '/' + pages[pages.length - 1].route;if (currentRoute === targetUrl) {return;}}// 优先使用navigateTo以获得更丝滑的切换;当接近栈上限时自动切换为reLaunch清栈this.navigateToSafe('auto', targetUrl);// 触发父组件事件this.$emit('tabChange', tabName);},/*** 安全导航(带防抖与自动方式)* @param {'auto'|'navigateTo'|'redirectTo'|'reLaunch'|'switchTab'} method* @param {string} url*/navigateToSafe(method, url) {if (this.isNavigating) return;this.isNavigating = true;const release = () => {setTimeout(() => {this.isNavigating = false;}, 300);};try {if (method === 'auto') {const pages = getCurrentPages();const len = pages.length;// 近栈上限走reLaunch,正常走navigateToif (len >= this.stackSoftLimit) {uni.reLaunch({ url, complete: release });} else {uni.navigateTo({ url, complete: release });}} else if (method === 'switchTab') {uni.switchTab({ url, complete: release });} else if (method === 'reLaunch') {uni.reLaunch({ url, complete: release });} else if (method === 'redirectTo') {uni.redirectTo({ url, complete: release });} else {uni.navigateTo({ url, complete: release });}} catch (e) {release();}},/*** 根据当前页面路径设置激活的tab*/setActiveTabByCurrentPage() {const pages = getCurrentPages();if (pages.length === 0) return;const currentPage = pages[pages.length - 1];const currentPath = currentPage.route;// 根据页面路径设置对应的tab状态if (currentPath.includes('pages/index/index')) {this.activeTab = 'home';} else if (currentPath.includes('pages2agree/pages/article/list') ||currentPath.includes('pages/article/index')) {this.activeTab = 'dynamic';} else if (currentPath.includes('pages/yzpg/index')) {this.activeTab = 'project';} else if (currentPath.includes('pages/profile/index') ||currentPath.includes('pages/profile/center') ||currentPath.includes('pages3profile/pages/profile/logged')) {this.activeTab = 'profile';}}}
};
</script><style lang="scss" scoped>
.custom-tabbar {position: fixed;bottom: 0;left: 0;right: 0;z-index: 999;background-color: #ffffff;box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
}/* 彩色装饰条 - 按设计稿样式 */
.color-bars {display: flex;height: 10rpx; /* 按设计稿5px转换为10rpx */background-color: #ffffff;
}.color-bar {height: 100%;
}.bar-orange {background-color: #f2981f; /* 按设计稿颜色 */flex: 350; /* 按设计稿比例 350:248:92 */
}.bar-brown {background-color: #ca5737; /* 按设计稿颜色 */flex: 248; /* 按设计稿比例 */
}.bar-red {background-color: #d84a24; /* 按设计稿颜色 */flex: 92; /* 按设计稿比例 */
}/* tabBar内容 */
.tabbar-content {display: flex;align-items: center;justify-content: space-between;padding: 20rpx 32rpx 26rpx 32rpx; /* 按设计稿调整内边距 */background-color: #ffffff;position: relative; /* 为绝对定位的帮助中心图标提供定位上下文 */padding-right: 160rpx; /* 右侧留出空间给帮助中心大图标 */
}.tab-item {display: flex;flex-direction: column;align-items: center;justify-content: center;flex: 1;position: relative;cursor: pointer;min-width: 0;transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);transform-origin: center;
}.tab-item:active {transform: scale(0.95);
}.tab-item.tab-active {transform: scale(1.05);
}.tab-icon {width: 56rpx; /* 按设计稿28px转换为56rpx */height: 56rpx; /* 按设计稿28px转换为56rpx */margin-bottom: 6rpx; /* 调整图标与文字间距 */transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);filter: grayscale(0);
}.tab-icon.icon-active {filter: grayscale(0) brightness(1.1);transform: scale(1.1);
}.tab-icon-uview {margin-bottom: 6rpx; /* 调整u-icon与文字间距 */
}.help-icon {width: 128rpx; /* 按设计稿64px转换为128rpx */height: 128rpx; /* 按设计稿64px转换为128rpx */display: block;object-fit: contain;margin-bottom: 6rpx; /* 与其他图标保持一致的间距 */
}.tab-text {font-size: 24rpx; /* 按设计稿12px转换为24rpx */color: #393939; /* 按设计稿未选中颜色 */line-height: 1;transition: color 0.3s ease;letter-spacing: 0.96rpx; /* 按设计稿letterSpacing 0.48转换 */
}.tab-text.active {color: #e63e0d; /* 按设计稿选中状态颜色 */font-weight: 600; /* 选中状态稍微加粗 */transform: scale(1.05);
}/* 帮助中心特殊样式 - 按设计稿64x64大图标 */
.help-center {position: absolute;right: 32rpx; /* 按设计稿调整距离右边缘的距离 */bottom: 26rpx; /* 与其他tab项对齐 */width: 128rpx; /* 按设计稿64px转换为128rpx */height: 156rpx; /* 为文字留出足够空间 */z-index: 15; /* 确保在最上层 */display: flex;flex-direction: column; /* 垂直排列 */align-items: center;justify-content: flex-start; /* 从顶部开始排列 */
}.help-center .help-icon {position: relative;z-index: 10;opacity: 1;visibility: visible;
}/* 帮助中心文字样式 */
.help-text {font-size: 24rpx; /* 与其他文字保持一致 */color: #393939; /* 与其他未选中文字保持一致 */line-height: 1;text-align: center;white-space: nowrap;letter-spacing: 0.96rpx; /* 与其他文字保持一致 */
}/* 响应式适配 */
@media screen and (max-width: 750rpx) {.tab-text,.help-text {font-size: 20rpx; /* 小屏幕下稍微缩小字体 */}.tab-icon {width: 48rpx; /* 小屏幕下稍微缩小图标 */height: 48rpx;}.tab-icon-uview {margin-bottom: 4rpx; /* 小屏幕下调整间距 */}/* 小屏幕下调整u-icon大小 */.tab-item .tab-icon-uview /deep/ .u-icon {font-size: 48rpx !important; /* 小屏幕下图标大小 */}.help-center {width: 112rpx; /* 小屏幕下帮助中心图标容器 */height: 140rpx; /* 小屏幕下高度 */right: 24rpx; /* 小屏幕下距离边缘 */}.help-icon {width: 112rpx; /* 小屏幕下帮助中心图标 */height: 112rpx;}.tabbar-content {padding: 16rpx 24rpx 20rpx 24rpx; /* 小屏幕下调整内边距 */padding-right: 140rpx; /* 小屏幕下右侧空间 */}.color-bars {height: 8rpx; /* 小屏幕下装饰条稍微细一些 */}
}/* 安全区域适配 */
@supports (padding-bottom: env(safe-area-inset-bottom)) {.tabbar-content {padding-bottom: calc(26rpx + env(safe-area-inset-bottom));}
}
</style>