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

uniapp微信小程序切換tabbar報錯:switchTab:fail timeout是為什么?

wx.switchTab跳轉(zhuǎn)成功,但tabBar選中狀態(tài)需要點兩次才會切換正常?

針對以上兩個常見的問題,以下代碼指出問題所在

<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: '預(yù)留',
        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: '自習(xí)',
        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)
  //錯誤原因:由于 手動維護(hù)的選中狀態(tài) 和 uni.switchTab 的頁面跳轉(zhuǎn)邏輯 不同步造成
  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 時出現(xiàn)選中狀態(tài)需要兩次點擊才能切換的問題,通常是由于 手動維護(hù)的選中狀態(tài) 和 uni.switchTab 的頁面跳轉(zhuǎn)邏輯 不同步造成的。以下是原因分析和解決方案:

問題原因分析

  1. 同步代碼與異步跳轉(zhuǎn)的時序沖突
    同步代碼立即執(zhí)行:currentIndex.value = index 是同步代碼,立即生效,此時 TabBar 的選中狀態(tài)會瞬間切換到目標(biāo)索引。
    異步跳轉(zhuǎn)延遲完成:uni.switchTab 是異步操作,觸發(fā)頁面跳轉(zhuǎn)后,實際頁面切換需要時間(如加載新頁面、執(zhí)行生命周期鉤子等)。
    關(guān)鍵問題:在跳轉(zhuǎn)完成前,currentIndex 的更新可能被后續(xù)邏輯覆蓋。

  2. 頁面生命周期鉤子覆蓋狀態(tài)
    目標(biāo)頁面的 onShow 未正確更新狀態(tài):
    當(dāng) uni.switchTab 跳轉(zhuǎn)完成后,目標(biāo)頁面會觸發(fā) onShow 生命周期鉤子。
    若未在 onShow 中根據(jù)當(dāng)前路徑更新 currentIndex,則可能出現(xiàn)以下情況:
    跳轉(zhuǎn)前手動設(shè)置的 currentIndex(如 index=1)已生效。
    跳轉(zhuǎn)完成后,目標(biāo)頁面未通過 onShow 更新 currentIndex(例如,仍保留舊值)。
    結(jié)果:TabBar 的選中狀態(tài)可能被重置為舊值,導(dǎo)致顯示狀態(tài)與實際路徑不一致。

  3. 框架內(nèi)部狀態(tài)與手動狀態(tài)的競爭
    原生 TabBar 與自定義 TabBar 的邏輯沖突:
    uni.switchTab 是小程序原生 API,它可能隱式更新原生 TabBar 的狀態(tài)(即使原生 TabBar 被隱藏)。
    若自定義 TabBar 的 currentIndex 未與原生邏輯完全解耦,可能導(dǎo)致:
    手動設(shè)置的 currentIndex 短暫生效。
    原生邏輯完成后(如頁面跳轉(zhuǎn)),框架內(nèi)部狀態(tài)覆蓋了 currentIndex。
    表現(xiàn):第一次點擊時,自定義 TabBar 狀態(tài)短暫切換后又被重置,需要第二次點擊才能同步

解決方案
方案 1:在跳轉(zhuǎn)成功回調(diào)中更新狀態(tài)
利用 uni.switchTab 的 success 回調(diào),確保頁面跳轉(zhuǎn)完成后再更新狀態(tài)。

function changeTab(item, index) {
  uni.switchTab({
    url: item.pagePath,
    success: () => {
      // 跳轉(zhuǎn)成功后再更新選中狀態(tài)
      currentIndex.value = index;
    },
    fail: (err) => {
      console.error('跳轉(zhuǎn)失敗:', err);
    }
  });
}

方案 2:完全由頁面路徑控制選中狀態(tài)
放棄手動維護(hù) currentIndex,改為在頁面的 onShow 生命周期中,根據(jù)當(dāng)前頁面路徑動態(tài)計算選中索引(或路徑字符串)。

//方案2.1:通過索引同步狀態(tài)
// 在自定義 TabBar 組件中
const routes = [  
  'pages/home/index',
  'pages/reserve/index',
  'pages/selfStudy/index',
  'pages/accompany/index',
  'pages/me/index',]; // TabBar 頁面路徑列表

// 在頁面生命周期中同步狀態(tài)
onShow() {
  // 獲取當(dāng)前頁面路徑
  const currentRoute = getCurrentPages().pop()?.route || '';
  // 根據(jù)路徑計算選中索引
  currentIndex.value = routes.findIndex(path => currentRoute.includes(path));
}

//方案2.2:通過字符串路徑同步狀態(tài)
// 修改后:currentIndex 存儲路徑字符串
const currentIndex = ref<string>('/pages/home/index');
<!-- 模板中判斷選中狀態(tài) -->
<view :class="{ 'active': currentIndex === item.pagePath }"></view>
// 在頁面生命周期中同步狀態(tài)
onShow(() => {
  const pages = getCurrentPages();
  currentIndex.value = `/${pages[pages.length - 1].route}`; // 或根據(jù)真實路徑更新
});

方案 3:wot-design-uni 的 tabbar 標(biāo)簽欄組件實現(xiàn)

<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中獲取當(dāng)前頁面路徑的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),否則微信開發(fā)者工具預(yù)覽二維碼會報下面的錯
Error: 系統(tǒng)錯誤,錯誤碼:800059,error: iconPath=, file not?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容