unibest-vue3-微信小程序自定义tabbar

uniapp微信小程序切换tabbar报错:switchTab:fail timeout是为什么?

wx.switchTab跳转成功,但tabBar选中状态需要点两次才会切换正常?

针对以上两个常见的问题,以下代码指出问题所在

<template>
  <view class="custom-tabbar">
    <template v-for="(item, index) in tabbarList" :key="index">
      <view
        v-if="item.isSpecial"
        class="tabbar-item relative top-21rpx left-8rpx"
        :class="{ active: currentIndex === index }"
        @click="changeTab(item, index)"
      >
        <image
          class="w-80rpx h-80rpx mb-10rpx"
          :src="currentIndex === index ? item.selectedIconPath : item.iconPath"
          mode="scaleToFill"
        ></image>
        <text class="text-22rpx">{{ item.text }}</text>
      </view>
      <view
        v-else
        class="tabbar-item relative top-38rpx"
        :class="{ active: currentIndex === index }"
        @click="changeTab(item, index)"
      >
        <image
          class="w-56rpx h-56rpx"
          :src="currentIndex === index ? item.selectedIconPath : item.iconPath"
          mode="scaleToFill"
        ></image>
        <text class="text-22rpx">{{ item.text }}</text>
      </view>
    </template>
  </view>
</template>

<script lang="ts" setup>
  const tabbarList = ref([
      {
        iconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/f30d1aa588d54f7abcd2c8876800dba8.png`,
        selectedIconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/7a39f803563c40dbbcd3e91f2ae8fbdb.png`,
        pagePath: '/pages/home/index',
        text: '首页',
        badge: 0,
        isDot: false,
      },
      {
        iconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/6372fa14d5a745f78a1891e0d92c6a2d.png`,
        selectedIconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/87a638c8d7e841a7aef8178f496121ed.png`,
        pagePath: '/pages/reserve/index',
        text: '预留',
        badge: 0,
        isDot: false,
      },
      {
        iconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/1bb14fd255e64b22a5265dfde7509e06.png`,
        selectedIconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/1bb14fd255e64b22a5265dfde7509e06.png`,
        pagePath: '/pages/selfStudy/index',
        text: '自习',
        isSpecial: true,
        badge: 0,
        isDot: false,
      },
      {
        iconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/dc356ca488cc42c2806e68e638f8b0e6.png`,
        selectedIconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/3a765bd86f2743029bc53bfe4075094b.png`,
        pagePath: '/pages/accompany/index',
        text: '陪伴',
        badge: 0,
        isDot: false,
      },
      {
        iconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/62f26d0570f546289d4c74f7ead1ec52.png`,
        selectedIconPath: `https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/e8c96e491dcb4aa4826fd8509ca482c1.png`,
        pagePath: '/pages/me/index',
        text: '我的',
        badge: 0,
        isDot: false,
      },
  ])
  const currentIndex = ref(0)

function changeTab(item, index) {
  console.log(item.pagePath, index)
  //错误原因:由于 手动维护的选中状态 和 uni.switchTab 的页面跳转逻辑 不同步造成
  currentIndex.value = index
  uni.switchTab({
    url: item.pagePath
  })
}
</script >

<style lang="scss" scoped>
.custom-tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  background-image: url('https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/f117faba0b4843ec986e70b04045bee7.png');
  background-size: cover;
  background-repeat: no-repeat;
  background-position: top center;
  height: 74px;
  padding-bottom: env(safe-area-inset-bottom);
  box-sizing: content-box;

  .tabbar-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #a3abd8;

    &.active {
      color: #30a4d7;
    }
  }
}
</style>
图示

在 Uniapp 中使用自定义 TabBar 时出现选中状态需要两次点击才能切换的问题,通常是由于 手动维护的选中状态 和 uni.switchTab 的页面跳转逻辑 不同步造成的。以下是原因分析和解决方案:

问题原因分析

  1. 同步代码与异步跳转的时序冲突
    同步代码立即执行:currentIndex.value = index 是同步代码,立即生效,此时 TabBar 的选中状态会瞬间切换到目标索引。
    异步跳转延迟完成:uni.switchTab 是异步操作,触发页面跳转后,实际页面切换需要时间(如加载新页面、执行生命周期钩子等)。
    关键问题:在跳转完成前,currentIndex 的更新可能被后续逻辑覆盖。

  2. 页面生命周期钩子覆盖状态
    目标页面的 onShow 未正确更新状态:
    当 uni.switchTab 跳转完成后,目标页面会触发 onShow 生命周期钩子。
    若未在 onShow 中根据当前路径更新 currentIndex,则可能出现以下情况:
    跳转前手动设置的 currentIndex(如 index=1)已生效。
    跳转完成后,目标页面未通过 onShow 更新 currentIndex(例如,仍保留旧值)。
    结果:TabBar 的选中状态可能被重置为旧值,导致显示状态与实际路径不一致。

  3. 框架内部状态与手动状态的竞争
    原生 TabBar 与自定义 TabBar 的逻辑冲突:
    uni.switchTab 是小程序原生 API,它可能隐式更新原生 TabBar 的状态(即使原生 TabBar 被隐藏)。
    若自定义 TabBar 的 currentIndex 未与原生逻辑完全解耦,可能导致:
    手动设置的 currentIndex 短暂生效。
    原生逻辑完成后(如页面跳转),框架内部状态覆盖了 currentIndex。
    表现:第一次点击时,自定义 TabBar 状态短暂切换后又被重置,需要第二次点击才能同步

解决方案
方案 1:在跳转成功回调中更新状态
利用 uni.switchTab 的 success 回调,确保页面跳转完成后再更新状态。

function changeTab(item, index) {
  uni.switchTab({
    url: item.pagePath,
    success: () => {
      // 跳转成功后再更新选中状态
      currentIndex.value = index;
    },
    fail: (err) => {
      console.error('跳转失败:', err);
    }
  });
}

方案 2:完全由页面路径控制选中状态
放弃手动维护 currentIndex,改为在页面的 onShow 生命周期中,根据当前页面路径动态计算选中索引(或路径字符串)。

//方案2.1:通过索引同步状态
// 在自定义 TabBar 组件中
const routes = [  
  'pages/home/index',
  'pages/reserve/index',
  'pages/selfStudy/index',
  'pages/accompany/index',
  'pages/me/index',]; // TabBar 页面路径列表

// 在页面生命周期中同步状态
onShow() {
  // 获取当前页面路径
  const currentRoute = getCurrentPages().pop()?.route || '';
  // 根据路径计算选中索引
  currentIndex.value = routes.findIndex(path => currentRoute.includes(path));
}

//方案2.2:通过字符串路径同步状态
// 修改后:currentIndex 存储路径字符串
const currentIndex = ref<string>('/pages/home/index');
<!-- 模板中判断选中状态 -->
<view :class="{ 'active': currentIndex === item.pagePath }"></view>
// 在页面生命周期中同步状态
onShow(() => {
  const pages = getCurrentPages();
  currentIndex.value = `/${pages[pages.length - 1].route}`; // 或根据真实路径更新
});

方案 3:wot-design-uni 的 tabbar 标签栏组件实现

<template>
  <view class="custom-tabbar">
    <wd-tabbar
      :model-value="currRoute"
      fixed
      :bordered="false"
      safeAreaInsetBottom
      placeholder
      active-color="#30A4D7"
      inactive-color="#A3ABD8"
      @change="onTabbarChange"
    >
      <template v-for="item in tabbarStore.tabbarList" :key="item.pagePath">
        <wd-tabbar-item
          custom-class="relative top--12rpx left-8rpx"
          v-if="item.isSpecial"
          :name="item.pagePath"
          :title="item.text"
        >
          <template #icon="{ active }">
            <image
              :src="active ? item.selectedIconPath : item.iconPath"
              class="size-80rpx"
              mode="scaleToFill"
            />
          </template>
        </wd-tabbar-item>
        <wd-tabbar-item v-else :name="item.pagePath" :title="item.text">
          <template #icon="{ active }">
            <image
              class="size-56rpx"
              :src="active ? item.selectedIconPath : item.iconPath"
              mode="scaleToFill"
            />
          </template>
        </wd-tabbar-item>
      </template>
    </wd-tabbar>
  </view>
</template>
<script lang="ts" setup>
import useTabbarStore from '@/store/tabbar'
const tabbarStore = useTabbarStore()
const activePages = getCurrentPages() //uniapp中获取当前页面路径的API
const currRoute = ref<string>()

function onTabbarChange(item) {
  uni.switchTab({
    url: `${item.value}`,
  })
}

onLoad(() => {
  currRoute.value = `/${activePages[activePages.length - 1].route}`
})
</script>

<style lang="scss" scoped>
.custom-tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  background-image: url('https://yanzhouedu-1330414272.cos.ap-guangzhou.myqcloud.com/2025/02/25/f117faba0b4843ec986e70b04045bee7.png');
  background-size: cover;
  background-repeat: no-repeat;
  background-position: top center;
  height: 74px;
  padding-bottom: env(safe-area-inset-bottom);
  box-sizing: content-box;
}
</style>

注意事项:
pages.config.ts文件中tabBar配置iconPath和selectedIconPath必须要填且不能是https链接形式(可以填写本地路径:static/images/tabbar/home.png),否则微信开发者工具预览二维码会报下面的错
Error: 系统错误,错误码:800059,error: iconPath=, file not?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容