小程序踩坑-tabbar

在与后台理顺授权、登录逻辑之后,就开始小程序的页面结构开发了。

既然用上了 vant,所以也就直接拿来用了 https://youzan.github.io/vant-weapp/#/tabbar。

使用vant组件库的好处,不用自己画图标,这对于一个开发人员的我来说,很重要不用自己自己画图标啊,自己画的是神马连自己都不知道。所以,带图标库的组件库,是令我非常欢喜的。

tabbar的使用需要结合wx小程序的官方文档 https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html

在 app.json 声明 tabbar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"tabBar": {
  "custom": true,
  "backgroundColor": "#ffffff",
  "list": [{
    "pagePath": "pages/about/about",
    "text": "关于"
  }, {
    "pagePath": "pages/home/home",
    "text": "首页"
  }, {
    "pagePath": "pages/my/my",
    "text": "我的"
  }]
},

wx对自定义的tabbar有相应的介绍,在代码根目录下添加入口文件

1
2
3
4
custom-tab-bar/index.js
custom-tab-bar/index.json
custom-tab-bar/index.wxml
custom-tab-bar/index.wxss

需要在 custom-tab-bar/index.json 中,增加 vant 依赖

1
2
3
4
"usingComponents": {
  "van-tabbar": "@vant/weapp/tabbar/index",
  "van-tabbar-item": "@vant/weapp/tabbar-item/index"
}

在 custom-tab-bar/index.wxml 增加

1
2
3
4
5
<van-tabbar active="" bind:change="onChange">
  <van-tabbar-item icon="search">关于</van-tabbar-item>
  <van-tabbar-item icon="home-o">首页</van-tabbar-item>
  <van-tabbar-item icon="search">我的</van-tabbar-item>
</van-tabbar>

在 custom-tab-bar/index.js 增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
data: {
  selected: 0,
  list: [
    "/pages/about/about",
    "/pages/home/home",
    "/pages/my/my"
  ]
}
...
{
  methods: {
    onChange(event) {
      const selected = event.detail
      this.setData({ selected })
      wx.switchTab({ url: this.data.list[selected]})
    }
  }
}

这里有会问题,tabbar的激活状态是不能正常显示的。

实际上 switchTab 切换过去时,会实例化一个 tabbar,实例化的新 tabbar 中的 selected 会默认为 0,所以会有这一问题。

wx 提供了 getTabbar() 方法来获取当前页面的 tabbar。所以,需要在 Page 完成初始化之后,调用

1
2
3
4
5
6
// /pages/about/about
Page({
  onShow: function() {
    this.getTabBar().setData({selected: 0})
  }
})

需要在每一个页面的 onShow 方法都调用一次,设置上不同的 selected 的值。但是,怎么才能自动地选择正确的 selected 的值呢?可以通过路由判断。怎么获取当前页面的路由呢?

注意到了 vant 示例中有代码块,帮忙解决了问题。

wx 可以通过 getCurrentPages() 得到 App 的页面栈。

PageObject[] getCurrentPages() 获取当前页面栈。数组中第一个元素为首页,最后一个元素为当前页面。

1
2
3
4
5
6
7
8
9
10
11
// custom-tab-bar/index.js 
// 在 methods 里增加
{
  methods: {
    init: {
      const page = getCurrentPages().pop();
      const selected = this.data.list.findIndex(item => item === `/${page.route}`)
      this.setData({ selected })
    }
  }
}

所以,可以将tab页面的 onShow 中的代码调整为

1
this.getTabBar().init()

即可,统一让 tabbar 的内部方法去处理 selected 的值。

但是,这样会有一个问题,Page 每次切换的时候,都会重新设置这个 selected 值,如果页面都自有 tabbar,能不能只设置一次呢?页面每次加载时会不会重新构建 tabbar 实例呢?

但是,通过日志查看,getTabBar() 中的 __wxWebviewId__ 是不变的,但奇怪的是 tabbar 中的 selected 的值是相互影响的。

即,切换过的 tab 页面后,三个页面只有三个 __wxWebviewId__的值,无论切换几次。但,如果从tab1切换到tab2后,在 Page => this.getTabBar().init() => this.selected 的值会是0, 而不是之前 tab2 实例化过的selected 值 1。

tabbar中的 __wxExparserNodeId__ 的值是三个,和 __wxWebviewId__ 是对应、固定的,难道 wx 对 tabbar 进行了 data 域的数据共享?

tabbar 挂载到页面上,会单独生成,不会重新生成,但会存在 data 域的数据共享。

另外在社区中发现,可以通过 Component 替换 Page,通过使用 Component 的 pageLifetimes => show方法来达到 Page 中的 onShow 效果。

Comments