微信小程序 自定义 tabBar

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

微信小程序一般分为页面、组件,各自有各自的特点,生命周期也不相同
现在要设计一个自定义的 tabBar,本来想写成一个页面,子页面都写成组件,但是这样就会损失页面的许多特性,比如生命周期等等
后面了解了下微信小程序的 tabBar ,发现微信小程序应该分为三种:tab页面、页面、组件
tab页面是啥?就是底部带 tabBar 的页面,只要按照微信小程序的配置,页面就能自动带上 tabBar,我称之为tab页面

底部的 tabBar 看起来只有一个?错
其实tab页面底部的 tabBar,就是一个组件,有多少个 tab页面就有多少个 tabBar!(每个tab页面可以通过wx.getTabBar()访问自己的 tabBar 实例)

按照微信小程序你可以弄个简单的tabBar,但是它是固定的,设置有多少个,底部就显示多少个,数量也有限制。但是可以自己做个自己的tabBar(微信小程序官网)

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

步骤一

按照原始的tabBar 配置,修改文件:/app.json
page 这里必须写上
tabBar.custom 为true,这个表面是自定义的 tabBar
tabBar.list 把所有的 tab页面 都列在这,自定义的只需要pagePath 注意前面没''。
系统会根据以上配置进行解析处理,这样就可以通过 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"
      },
      {
        "pagePath": "pages/index2/index2"
      },
      {
        "pagePath": "pages/index3/index3"
      },
      {
        "pagePath": "pages/index4/index4"
      }
    ]
  }
}

步骤二

和 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>

效果

注意设置设置底部菜单的时候,要 wx.relaunch(),这样才可以清除缓存的影响,因为 tab 页面是会缓存的

其他

可以不清除缓存,直接设置吗?可以
但我没实现(因为不需要),我这里实际上每个 tabBar 实例,只设置了一次data,后续就没管了
而且,tabBar 实例应该和页面路径相匹配,我这里匹配不了,因为只有页面才有资格获取路径

如何实现呢?讲讲方式,因为每个 tabBar 实例,并不能够获取当前页面路径。只有页面才能够获取
所以设置 tabBar 实例的时候,可以通过页面来设置,前面讲到只要是 tab页面tab页面可以通过wx.getTabBar()获取属于它的 tabBar 实例
每次点击这个页面的时候(也就是 onShow 的时候),就动态设置tabBar的值,参考这个同学

posted @ 2024-01-07 22:14  Sebastian·S·Pan  阅读(229)  评论(0编辑  收藏  举报