微信小程序 自定义 tabBar

发布时间 2024-01-07 22:18:14作者: Sebastian·S·Pan

最近开发又涉及到了微信小程序,好多年没写过小程序了

现在要设计一个自定义tabBar,本来想写成一个页面,子页面都写成组件,但是这样就会损失页面的许多特性,比如生命周期等等
后面了解了下 wx 的 tabBar 原理,其实每个tab页面(就是底部带 tabBar 的页面,可以通过 wx.switchTab()访问,这里和普通的页面加以区分),底部的 tabBar 都是单独的
什么意思?也就是说,多个tab页面底部的tabBar不是通用的,tabBar 是该页面的组件,是一部分,tab页面可以通过 this.getTabBar() 获取属于自己的 tabBar

当你使用自定义tabBar的时候,tab 页面就会自动带上这个组件,当然自定义tabBar是必须由你自己完成实现

看了一些其他人的实现,感觉有点啰嗦,设计的也不完美
这里整合一下我写的,步骤也简单,两个步骤:

步骤一

app.json 中配置好,主要是配置两个
page 这里必须写上
tabBar.custom 为true,这个表面是自定义的 tabBar
tabBar.list 把所有的 tab页面 都列在这,注意前面没''。这必须写,系统会解析处理,这样就可以通过 wx.switchTab 跳转到 tab页面了, page就会自动带上自定义tabBar

{
  "pages": [
    "pages/index1/index1",
    "pages/index2/index2",
    "pages/index3/index3",
    "pages/index4/index4"
  ],
  "tabBar": {
    "custom": true,
    "list": [
      {
        "pagePath": "pages/index1/index1",
        "iconPath": "/assets/首页.png",
        "selectedIconPath": "/assets/首页-active.png",
        "text": "按钮1"
      },
      {
        "pagePath": "pages/index2/index2",
        "iconPath": "/assets/首页.png",
        "selectedIconPath": "/assets/首页-active.png",
        "text": "按钮2"
      },
      {
        "pagePath": "pages/index3/index3",
        "iconPath": "/assets/首页.png",
        "selectedIconPath": "/assets/首页-active.png",
        "text": "按钮3"
      },
      {
        "pagePath": "pages/index4/index4",
        "iconPath": "/assets/首页.png",
        "selectedIconPath": "/assets/首页-active.png",
        "text": "按钮4"
      }
    ]
  }
}

步骤二

和 pages 文件夹并列,建立文件夹custom-tab-bar,里面命名为 index的组件,里面我还额外建立了一个文件customTabBar.ts(使用 js 用 js 命名就可以了),以下是各个文件的代码:

代码

customTabBar.ts

// tabBar的data
export const tabBarData: {
  listIndex: number,// 底部高亮下标
  color: string,
  selectedColor: string,
  backgroundColor: string,
  list: Array<{
    pagePath: string,
    iconPath: string,
    selectedIconPath: string,
    text: string,
  }>
} = {
  listIndex: 0,
  color: "#5F5F5F",
  selectedColor: "#07c160",
  backgroundColor: "#F7F7F7",
  list: []
}

// 微信的setData貌似不会对原有的对象进行处理,直接引用上面的tabBarData
// 我舍弃了下面这个方法,毕竟会有一丢丢的性能损失?
// export const getTabBarData = () => JSON.parse(JSON.stringify(tabBarData));

// 所有的list,和 app.json 保持一致
const list = [
  {
    "pagePath": "pages/index1/index1",
    "iconPath": "/assets/首页.png",
    "selectedIconPath": "/assets/首页-active.png",
    "text": "按钮1"
  },
  {
    "pagePath": "pages/index2/index2",
    "iconPath": "/assets/首页.png",
    "selectedIconPath": "/assets/首页-active.png",
    "text": "按钮2"
  },
  {
    "pagePath": "pages/index3/index3",
    "iconPath": "/assets/首页.png",
    "selectedIconPath": "/assets/首页-active.png",
    "text": "按钮3"
  },
  {
    "pagePath": "pages/index4/index4",
    "iconPath": "/assets/首页.png",
    "selectedIconPath": "/assets/首页-active.png",
    "text": "按钮4"
  }
]

// 根据角色设置合适的 list,并更新 tabBarData
// 注意每个 tab 页面,都有自己的独立的 tabbar 组件
// 注意每次设置后都要 wx.relaunch,清空页面缓存
export const setCustomTabBarByRole = (role: 'member' | 'tourist', defaultIndex = 0) => {
  tabBarData.listIndex = defaultIndex;
  if (role === 'member') {
    tabBarData.list = list.slice();
  } else if (role === 'tourist') {
    tabBarData.list = list.slice(0,3);
  }
}

export const changeCustomTabBarIndex = (index: number) => {
  const targetNav = tabBarData.list[index];
  if (targetNav) {
    tabBarData.listIndex = index;
    const { pagePath } = targetNav;
    wx.switchTab({
      // 注意这里路径要加上 '/',而 app.json 不能加上 '/',会报错!
      url: '/' + pagePath
    })
  }
}

index.less

/* custom-tab-bar/index.wxss */
.tabBar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 48px;
  background: white;
  display: flex;
  padding-bottom: env(safe-area-inset-bottom);
  border-top: 1px solid #c1c1c1;
}

.tabBarItem {
  flex: 1;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.itemImage {
  width: 26px;
  height: 26px;
}

.itemTitle {
  font-size: 10px;
}

index.ts

// custom-tab-bar/index.ts
import { tabBarData, changeCustomTabBarIndex } from "./customTabBar";
Component({
  attached() {
    // 经过实验,微信小程序页面、组件,data只支持字面量初始化,不支持函数初始化
    // 所以在这个时机进行初始化data!
    // 注意 setData函数 不会对参数造成影响,所以这里直接放入tabBarData
    // 而不是通过 JSON.parse(JSON.stringify(obj)) 复制一份
    this.setData(tabBarData);
  },
  methods: {
    switchTab(event: any) {
      const { index } = event.currentTarget.dataset;
      changeCustomTabBarIndex(index);
    },
  }
})

index.wxml

<!--custom-tab-bar/index.wxml-->
<view class="tabBar">
  <view class="tabBarItem" wx:for="{{list}}" wx:key="index" data-index="{{index}}" bindtap="switchTab">
    <image class="itemImage" src="{{listIndex === index ? item.selectedIconPath : item.iconPath}}"></image>
    <view class="itemTitle" style="color: {{listIndex === index ? selectedColor : color}}">{{item.text}}</view>
  </view>
</view>