之前的专栏文章有提到过引入三方组件库的必要性,因为官方的很多组件扩展性很差,甚至很多常用的组件都没有,比如 popup,自己又不想重复造轮子(目前的前端实力也不允许~)。
但是引入组件库之后,又会导致一些其他问题,就拿 tabbar 来说,原生的 tabbar,只需要在app.json 文件里定义好 tabbar 的配置,然后在具体的 page 里写逻辑就好了,框架会自动判断当前 tab 页并渲染,然后执行对应 page 的逻辑代码,如下:
"tabBar": {
"list": [{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "./icons/home.png",
"selectedIconPath": "./icons/home-fill.png"
},
{
"pagePath": "pages/create/create",
"text": "创建活动",
"iconPath": "./icons/create.png",
"selectedIconPath": "./icons/creating.png"
}]
}
但是引入三方组件库的 tabbar,就相当于使用了自定义组件,而小程序对自定义 tabbar 的支持并友好,比如切换tab的焦点page的渲染这种基础能力都不提供,实在是无力吐槽。
你可能会说不是有 wx.switchTab() 方法吗,在 onChange(event) 监听事件里设置一下 url 不就好了?我开始也是这么想的,但是发现每次 switchTab 后,active 的 page 和 event 里的页面索引是不一致的。为了解决这个问题,花了我不少时间,下面贴一下我各种搜索+调试出的解决方法。
先上效果图: 代码在 GitHub 上同步更新,所以文章中略过。代码传送门
Step1:
在 pages 同级目录新增 custom-tab-bar 目录,以及配套的四个文件(js、json、wxml、wxss),然后在 app.json 里配置默认值,其实就和自定义组件用法很像,只不过这里对于自定义 tabbar 有很多硬性要求,比如目录名称、所在路径和配置项等(都必须一致!)。详见官方文档
Step2:
引入 vant 组件库,引入方法在 vant 文档都写的很清楚了 vant传送门
Step3:
关键部分是引入组件库后 custom-tab-bar/index.js 的内容,这里代码还是要贴出来讲一下的:
Page({
data: {
active: 'home',
createIcon: 'add-o',
tabs: {
home: "/pages/home/home",
create: "/pages/create/create",
square: "/pages/square/square",
mine: "/pages/mine/mine"
}
},
onChange(event) {
wx.switchTab({
url: this.data.tabs[event.detail]
})
}
});
- active 是 vant 组件库对 tab 的索引名(和官方自定义 tabbar 的 index 一样的含义,用于确定唯一的 tab),默认是 number,0123这种,因为我用的 name 索引方式,所以这里是 string 类型
- createIcon 是为了动态修改创建活动 tab 的图标,提取出来的变量,在渲染 tab 的时候会一起改掉
- tabs 是每个 tab 页跳转的路由具体路径
- onChange 方法是在
custom-tab-bar/index.wxml 里绑定的监听事件,当切换 tab 的时候会触发 - event.detail 就是触发的时候的 tab 索引名,这里用来找到对应的 pagePath,进行跳转
Step4:
在每一个路由 page 页面,如 pages/create/create.js 里的 onShow() 事件,通过 getTabBar().setData() 方法设置 tab 索引,如果有别的 data 属性,比如这里的 create 页面要在点击 tab 的时候修改图标,也可以一起赋值。
Page({
data: {},
onShow: function () {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({
active: 'create',
createIcon: 'edit'
})
}
}
});
总结:
这个方法的关键在于,抛弃原有在 onChange 里直接设置 active 属性的方式,因为和 switchTab 会冲突,导致索引被重置。而通过监听 tab 的 onShow 事件,利用事件执行的先后顺序这个特点,来规避这个问题。
其实吧,在小程序官方文档里是有相关说明的:
… 为了保证低版本兼容以及区分哪些页面是 tab 页,tabBar 的相关配置项需完整声明,但这些字段不会作用于自定义 tabBar 的渲染。 … 注意:如需实现 tab 选中态,要在当前页面下,通过 getTabBar 接口获取组件实例,并调用 setData 更新选中态。
注意这两句话,第一句话解释了为什么要在 app.json 里配置那些其实不生效的默认配置,只是为了兼容。第二句话提示了更新选中态的方法,只是 vant 组件库文档默认在 onChange() 事件里更新选中态,新手开箱即用就会被误导(比如我),从而很容易忽略官方这段话。
有些坑必须得踩一下才能记忆犹新啊~
后记:
tab 页面在第一次加载的时候,并不是流畅,后面如果找到解决方法会同步更新。
|