微信小程序自定義 tabbar


一定的需求情況下,無法使用小程序原生的 tabbar 的時候,需要自行實現一個和 tabbar 功能一模一樣的自制組件。

查閱了海量的博客和文檔之后,親自踩坑。總結了三種在不使用微信小程序原生 tabbar的情況下自制 tabbar 的方法。並說說這幾種方法各自的特色。

類 navigator 跳轉方式

類 navigator 跳轉方式是我自己起的名稱,因為它的實現思路就是這個樣子的。期初參考 微信小程序自定義tabBar組件開發 這篇博文的思路。進行了這種方式的嘗試,並為后續提供了解決思路。在這次實踐的過程中使用了和該博文類似的目錄結構。

 

template 文件主要包含了 tabbar 的內容、邏輯、模板、樣式。

tabbar_template.js

//初始化數據
function tabbarinit() {
  return [
    {
      "current": 0,
      "pagePath": "/pages/travel_shop/travel/travel_index/travel_index",
      "iconPath": "/pages/img/tab_icon_home@2x.png",
      "selectedIconPath": "/pages/img/tab_icon_home_sel@2x.png",
      "text": "首頁"
    },
    {
      "current": 0,
      "pagePath": "/pages/travel_shop/travel/travel_car/travel_car",
      "iconPath": "/pages/img/tab_icon_shop@2x.png",
      "selectedIconPath": "/pages/img/tab_icon_shop_sel@2x.png",
      "text": "購物車"
    },
    {
      "current": 0,
      "pagePath": "/pages/travel_shop/travel/travel_my/travel_my",
      "iconPath": "/pages/img/tab_icon_my@2x.png",
      "selectedIconPath": "/pages/img/tab_icon_my_sel@2x.png",
      "text": "我的"
    }
  ]

}
//tabbar 主入口
function tabbarmain(bindName = "tabdata", id, target) {
  var that = target;
  var bindData = {};
  var otabbar = tabbarinit();
  otabbar[id]['iconPath'] = otabbar[id]['selectedIconPath']   //換當前的icon
  otabbar[id]['current'] = 1;
  bindData[bindName] = otabbar
  that.setData({ bindData });
}

module.exports = {
  tabbar: tabbarmain
}

 

 

tabbar_template.wxml

<template name="tabBar">
  <view class="tabBar">
    <block wx:for="{{tabBar}}" wx:for-item="item" wx:key="tabBar">
        <view class="tabBar-item">
            <navigator open-type="reLaunch" url="{{item.pagePath}}">
                <view><image class="tabBar-icon" src='{{item.iconPath}}'></image></view>
                <view class="{{item.current== 1 ? 'tabBartext' :''}}">{{item.text}}</view>
            </navigator>
        </view>
    </block>
 </view>
</template>

 

tabbar_template.wxss

.tabBar-icon{
  width:54rpx;
  height: 54rpx;
}
.tabBar{
  width:100%;
  position: fixed;
  bottom:0;
  padding:10rpx;
  margin-left:-4rpx;
  background:#F7F7FA;
  font-size:24rpx;
  color:#8A8A8A;
  box-shadow: 3rpx 3rpx 3rpx 3rpx #aaa; 
  z-index: 9999;
}

 .tabBar-item{
  float:left;
  width: 33.333%;
  text-align: center;
  overflow: hidden;
}
/*當前字體顏色*/
.tabBartext{
  color: black;
}
.navigator-hover{
  background-color: rgba(0, 0, 0, 0);
}

 

而后在全局引入樣式

@import "/pages/travel_shop/travel/tabbar_template/tabbar_template.wxss";

 

並在每一個頁面的子文件(wxml、JS)中引入相應的內容

wxml 引入

<import src="/pages/travel_shop/travel/tabbar_template/tabbar_template.wxml"/>
<template is="tabBar" data="{{tabBar:bindData.tabBar}}"/>

 

JS 引入

var template = require("../tabbar_template/tabbar_template.js");

並在對應的 onLoad 生命周期中,注明它是哪一個 tabbar

  onLoad: function (options) {
    template.tabbar("tabBar", 1, this) //0表示第一個tabbar,這里1表示第二個 tabbar 的 icon
  },

 

效果預覽

 

我們最終得到了效果,但這種效果帶了明顯的抖動閃爍。原因則是因為這種實現方式的本質是通過 navigator 和 JS 事件觸發實現頁面之間的跳轉。因此我開始找尋另一種實現的方式。在 微信小程序自定義tabBar組件開發 這篇博客的留言板,我發現該文的作者也發現了這種方式的不足,並提到可以通過可以把頁面都寫成組件 component 的方式實現更好的效果。

 

template 模板 / component 組件

在繼續查閱了一些關於小程序自定義 tabbar 的博客之后,找到了 微信小程序 - 自定義tabbar 這篇博文。按照這篇博文描述的結構,我也進行了嘗試。發現這種方式不會出現之前跳轉產生的那種閃爍現象出現。

 

之后再查閱 微信小程序 template 模板與 component 組件的區別和使用 這篇博文的時候了解到,如果當我們主要是為了展示頁面的時候,可以使用 template 方式。如果涉及到 tabbar 對應各個頁面的業務邏輯交互比較多,那就最好使用 component 組件。

因為這三個頁面涉及到了很多獨立的交互,所以我決定使用 component 組件的形式,將自定義的 tabbar 寫成一個頁面,然后將其他三個 tabbar 按鈕對應的頁面寫成三個 component 組件。這種方法和 Vue 中的組件化很相似,可以把單個組件文件夾當成 Vue 中的一個 .vue 文件。

component 與普通 page 類似,但是 JS 文件和 JSON 文件與頁面不同。

小程序組件 JS 模板

Component({
  /* 開啟全局樣式使用 */
  options: {
    addGlobalClass: true,
  },

  /* 組件的屬性列表 */
  properties: {
    name: {
      type: String,
      value: ''
    }
  },

  /* 組件的初始數據 */
  data: {

  },

  /* 生命周期函數 */
  lifetimes: {
    attached: function () { },
    moved: function () { },
    detached: function () { },
  },

  /* 組件的方法列表 */
  methods: {

  },
})

 

component 組件 JSON 文件

{
  "component": true,
  "usingComponents": {}
}

 

tabbar 引用和配置

引用組件 JSON

按照如圖的結構,三個 component 作為子組件,tabber 作為一個父級,因此它的 JSON 需要引入這三個 component 組件。

 
// travel.json
{
  "usingComponents": {
    "travel_car": "travel_car/travel_car",
    "travel_index": "travel_index/travel_index",
    "travel_my": "travel_my/travel_my"
  }
}

 

tabbar JS

而該頁面的 JS 僅僅只用來控制 tabbar 的 icon 選擇,和傳遞一個 index 告訴頁面該隱藏和顯示哪一個 component 組件。

// travel.js
let app = getApp()

Page({
  data: {
    currentTab: 0,
    items: [
      {
        "iconPath": "/pages/img/tab_icon_home@2x.png",
        "selectedIconPath": "/pages/img/tab_icon_home_sel@2x.png",
        "text": "首頁"
      },
      {
        "iconPath": "/pages/img/tab_icon_shop@2x.png",
        "selectedIconPath": "/pages/img/tab_icon_shop_sel@2x.png",
        "text": "購物車"
      },
      {
        "iconPath": "/pages/img/tab_icon_my@2x.png",
        "selectedIconPath": "/pages/img/tab_icon_my_sel@2x.png",
        "text": "我的"
      }
    ]
  },
  //事件處理函數
  bindChange: function (e) {
    let that = this;
    that.setData({
      currentTab: e.detail.current
    });
  },
  swichNav: function (e) {
    let that = this;
    if (this.data.currentTab === e.target.dataset.current) {
      return false;
    } else {
      that.setData({
        currentTab: e.target.dataset.current
      })
    }
  },
  onLoad: function () {
    let that = this
    app.getUserInfo(function (userInfo) {
      that.setData({
        userInfo: userInfo
      })
    })
  }
})
 
        

 

tabbar WXML

直接使用之前 JSON 中引用過的標簽名,類似於 Vue 中使用模板標簽。這里由於組件模板標簽不支持直接使用 hidden 屬性,所以在外包裹了一層 view 標簽用來添加 hidden屬性。

<view hidden="{{currentTab == 0? false: true}}">
  <travel_index/>
</view>
<view hidden="{{currentTab == 1? false: true}}">
  <travel_car/> 
</view>
<view hidden="{{currentTab == 2? false: true}}">
  <travel_my/>
</view>

<view class="nav-tabs">
  <view class="tab-list {{currentTab == idx ? 'active' : 'default' }}" wx:for="{{items}}" wx:key="prototype" wx:for-index="idx" wx:for-item="item" data-current="{{idx}}" bindtap="swichNav">
    <text class="tab-text" wx:for-index="idx" data-current="{{idx}}" src="{{currentTab == idx ? item.selectedIconPath : item.iconPath }}">{{item.text}}</text>
    <image class="iconPath" wx:for-index="idx" data-current="{{idx}}" src="{{currentTab == idx ? item.selectedIconPath : item.iconPath }}"></image>
  </view>
</view>

 

tabbar WXSS

Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors.(./pages/xxx/xxx.wxss:288:3)This wxss file is ignored.

造成這種報錯的原因是 component 組件的樣式中不能包含一些特定的選擇器。

page {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.nav-tabs {
  width: 100%;
  display: flex;
  position: fixed;
  bottom: 0;
}

.tab-list {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column-reverse;
  background: #fcfcfc;
}

.tab-text {
  font-size: 24rpx;
  line-height: 35rpx;
  color: #5f5f5f;
}

.iconPath {
  width:54rpx;
  height: 54rpx;
}

.tab-content {
  flex: 1;
}

.default {
  line-height: 75rpx;
  text-align: center;
  flex: 1;
  color: #eee;
  font-weight: bold;
  font-size: 28rpx;
}

.active {
  line-height: 75rpx;
  text-align: center;
  color: black;
  flex: 1;
  font-weight: bold;
  font-size: 28rpx;
}

.show {
  display: block;
  flex: 1;
}

.hidden {
  display: none;
  flex: 1;
}

 

預覽效果

最終就完成了一個非原生小程序 tabbar 的自定義 tabbar 。

 

 

Github

在這篇文章發布之后,有一些朋友詢問。 我重新整理了一個比較清晰整潔的 Demo 發布在了 GitHub
如果這個 Demo 能夠幫助到您。請不要吝惜您的 Star😊。

Demo地址:https://github.com/evenyao/wx-diy-tabbar

 

參考

微信小程序自定義tabBar組件開發
微信小程序 - 自定義tabbar
微信小程序 template 模板與 component 組件的區別和使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM