前言
在写公司项目时,遇到了一个需求: 用户登录后,如果这个用户有供应商身份,则在个人资料下显示切换按钮,点击切换后小程序底部的tabbar要变成其他的,接下来就跟大家分享下我的实现方式,欢迎各位感兴趣的开发者阅读本文。
自定义tabbar
刚开始求助别人时,他们说我这个需求需要通过自定义tabbar实现。
安装vant/weap
此处使用的tabbar使用vant提供的,所以需要安装下。
- 在终端执行下述命令
yarn add @vant/weapp --production
- 如果你的小程序是npm构建方式的话,可参考vant官网提供的引入方式: 通过npm在项目中引入vant
- 我的项目没有使用npm构建,所以采用本地安装的形式,按照下述操作进行,即可完成本地安装。
1. 在本地的npm库下找到我们通过yarn安装的vant-webapp包
2. 在项目的根目录下创建plguins文件夹,然后把第一步找到的vant-webapp文件夹复制到这里来
3. 安装完成,此时的目录结构是这样的
配置custom-tabbar
按照小程序官网的custom-tabbar所述,我们需要进行一些配置,才能让小程序识别到我们的自定义tabbar。
- 在app.json中的tabbar项添加custom字段和vant的tabbar声明
// app.json
{
// *********** 其他项省略 ******* //
"tabBar": {
// custom设置为true,小程序就会项目目录找custom-tab-bar文件夹
"custom": true,
"color": "#CCCCCC",
"selectedColor": "#1296db",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "images/icon/vegetables.jpg",
"selectedIconPath": "images/icon/vegetables_selected.png"
},
{
"pagePath": "pages/shopping/shopping",
"text": "购物车",
"iconPath": "images/icon/shopping.png",
"selectedIconPath": "images/icon/shopping_selected.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "images/icon/my.jpg",
"selectedIconPath": "images/icon/my_selected.png"
}
]
},
"usingComponents": {
"van-tabbar": "/plguins/vant-weapp/dist/tabbar/index",
"van-tabbar-item": "/plguins/vant-weapp/dist/tabbar-item/index"
}
}
- 在项目根目录创建custom-tab-bar文件夹,并添加对应的wxml、wxss、js、json文件
- 编写 tabBar 代码
<!---wxml部分-->
<van-tabbar
active="{{ active }}"
bind:change="onChange"
>
<van-tabbar-item icon="home-o">首页</van-tabbar-item>
<van-tabbar-item icon="search">购物车</van-tabbar-item>
<van-tabbar-item icon="friends-o">我的</van-tabbar-item>
</van-tabbar>
Page({
data: {
active: 0,
},
onChange(event) {
this.setData({ active: event.detail });
switch ( event.detail) {
case 0:
// 首页
wx.switchTab({
url:''
});
break;
case 1:
// 购物车
wx.switchTab({
url:''
});
break;
case 2:
// 我的
wx.switchTab({
url:''
});
break;
}
}
});
执行结果
本来我还满心欢喜的觉得,这个需求好简单,这么容易就实现了,结果编译后,我心态崩了,这用户体验也太差了吧… 我点击tabbar后,页面切换了,tabbar却没反应过来…
自定义组件
使用自定义tabbar形式实现,用户体验是很不好的,于是就只能找另一种解决方案了,经过一番查找后,答案指向了自定义组件,这种方案是比较麻烦的,需要将项目的原面进行重构,由Page方式改成Component形式。
实现过程
-
我们还是使用vant提供的tabbar组件
-
因为不需要微信提供的tabbar,所以删除掉app.json中的tabBar相关内容
-
在page下创建main文件夹,并创建对应的小程序所需文件,如下图所示
-
编写组件相关代码
<!-- main/index.wxml -->
<view class='main-wrapper' style='margin-bottom:{{tabbarHeight}}px;'>
<Index wx:if='{{isLogin === false}}'></Index>
<!--主页-->
<home wx:if='{{activeIndex === 0 && isLogin===true && isSupplier===false}}' onShow="{{tabbar[0].selected}}"></home>
<!--商品-->
<commodity wx:if='{{activeIndex === 0 && isLogin===true && isSupplier===true}}' onShow="{{otherBar[0].selected}}"></commodity>
<!--购物车-->
<shopping wx:if='{{activeIndex === 1 && isLogin===true && isSupplier === false}}' onShow="{{tabbar[1].selected}}"></shopping>
<!--订单-->
<order wx:if='{{activeIndex === 1 && isLogin===true && isSupplier===true}}' onShow="{{otherBar[1].selected}}"></order>
<!--我的-->
<user wx:if='{{activeIndex === 2 && isLogin===true}}' onShow="{{tabbar[2].selected}}"></user>
</view>
<van-tabbar wx:if="{{isLogin === true && isSupplier === false}}" active="{{ activeIndex }}" bind:change="onChange" z-index="9999">
<van-tabbar-item wx:for="{{tabbar}}" wx:key="index" info="{{item.tips}}">
<image
slot="icon"
src="{{item.imgSrc}}"
mode="aspectFit"
style="width: 30px; height: 18px;"
/>
<image
slot="icon-active"
src="{{item.selectImgSrc}}"
mode="aspectFit"
style="width: 30px; height: 18px;"
/>
{{item.name}}
</van-tabbar-item>
</van-tabbar>
<van-tabbar wx:if="{{isLogin === true && isSupplier === true}}" active="{{ activeIndex }}" bind:change="onChange" z-index="9999">
<van-tabbar-item wx:for="{{otherBar}}" wx:key="index" info="{{item.tips}}">
<image
slot="icon"
src="{{item.imgSrc}}"
mode="aspectFit"
style="width: 30px; height: 18px;"
/>
<image
slot="icon-active"
src="{{item.selectImgSrc}}"
mode="aspectFit"
style="width: 30px; height: 18px;"
/>
{{item.name}}
</van-tabbar-item>
</van-tabbar>
// main/index.js
/*
因为此处小程序的授权登录页面也在main里进行引导,所以这个文件我们做了如下操作:
1. 我们需要在app.js创建一个全局变量isLogin
2. 在此处监听isLogin是否改变
3. 如果改变就改变data中定义的isLogin属性,wxml页面就会对应的显示tabbar并进入home组件
*/
const app = getApp();
Page({
data: {
tabbar: [
{
name: "首页",
tips:'',
selected: false,
imgSrc:"/images/icon/vegetables.jpg",
selectImgSrc:"/images/icon/vegetables_selected.png"
},
{
name: "购物车",
tips:'',
selected: false,
imgSrc:"/images/icon/shopping.png",
selectImgSrc:"/images/icon/shopping_selected.png"
},
{
name: "我的",
tips: '',
selected: false,
imgSrc:"/images/icon/my.jpg",
selectImgSrc:"/images/icon/my_selected.png"
}
],
otherBar:[
{
name: "商品",
tips: '',
selected: false,
imgSrc:"/images/icon/commodity.png",
selectImgSrc:"/images/icon/commodity_selected.png"
},
{
name: "订单",
tips: '',
selected: false,
imgSrc:"/images/icon/order.png",
selectImgSrc:"/images/icon/order_selected.png"
},
{
name: "我的",
tips: '',
selected: false,
imgSrc:"/images/icon/my.jpg",
selectImgSrc:"/images/icon/my_selected.png"
}
],
tabbarHeight: app.isIPhoneX ? 84 : 50, // 底部tabbar高度
activeIndex: 0, // 选中的tab
scrollTopArray: [], // 记录每个页面的滚动位置
isLogin: app.globalData._isLogin,
isSupplier: app.globalData._isSupplier,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.data.tabbar.forEach((item, index, arr) => {
this.data.scrollTopArray[index] = 0;
// item.isFirstLoad = true
});
wx.setNavigationBarTitle({
title: this.data.tabbar[0].name,
})
},
//定义监听回调方法
//app 监听回调方法
watchBack: function (value) { //这里的value 就是 app.js 中 watch 方法中的 set, 返回整个 globalData
this.setData({
isLogin: value._isLogin,
isSupplier: value._isSupplier
});
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
const self = this;
this.updateSubPageShowHide(this.data.activeIndex);
// 监听全局变量的改变
getApp().watch(self.watchBack);
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
onChange(event) {
if (event.detail == this.data.activeIndex) return;
this.updateSubPageShowHide(event.detail);
this.setData({
activeIndex: event.detail,
pageName: this.data.tabbar[event.detail].name
});
// 还原子页面的滚动位置
wx.pageScrollTo({
duration: 0,
scrollTop: this.data.scrollTopArray[event.detail]
})
},
// 记录每个子页面的滚动位置
onPageScroll(e) {
this.data.scrollTopArray[this.data.activeIndex] = e.scrollTop;
},
// 更新组件的show hide 生命周期
updateSubPageShowHide(currentIndex) {
this.data.tabbar.forEach(function (value, i) {
if (i == currentIndex) {
value.selected = true;
wx.setNavigationBarTitle({
title: value.name,
})
} else {
value.selected = false;
}
});
this.setData({
tabbar: this.data.tabbar,
})
},
});
// main/index.json, 此处用tabbar对应的组件声明
{
"usingComponents": {
"home":"/pages/home/home",
"shopping": "/pages/shopping/shopping",
"user": "/pages/user/user",
"Index": "/pages/index/index",
"commodity": "/pages/commodity/commodity",
"order": "/pages/commodityOrder/commodityOrder"
}
}
// app.js
/*
由于main/index.js中使用了全局变量isLogin和isSupplier,我们需要做如下操作
1. 在app.js中添加对应的全局变量
2. 创建监听方法,监听这两个全局变量的改变
*/
globalData: {
userInfo: null,
// 是否登录
_isLogin:false,
// 是否供应商
_isSupplier:false,
data:{
}
},
watch: function (method) {
let obj = this.globalData;
Object.defineProperty(obj, "data", { //这里的 data 对应 上面 globalData 中的 data
configurable: true,
enumerable: true,
set: function (value) { //动态赋值,传递对象,为 globalData 中对应变量赋值
this._isLogin = value.isLogin;
this._isSupplier = value.isSupplier;
method(value);
},
get: function () { //获取全局变量值,直接返回全部
return this.globalData;
}
})
}
// tabbar对应的页面改造成组件的示例
import base from "../../api/base";
var app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
height: {
type: Number,
value: app.homePageHeight
},
onShow: {
type: Boolean,
value: false,
observer: 'onShowHideChange'
},
},
/**
* 页面的初始数据
*/
data: {
total: 0,
size: 0,
shoppingList: {},
baseUrl:base.defaultBaseUrl
},
/**
* 组件的方法列表
*/
methods: {
onShowHideChange(show) {
if(show){
// 组件显示时执行这里
this.onShow();
}else{
}
},
},
onShow: function () {
this.initData();
},
}
});
执行结果
执行后,效果还不错,功能都实现了,但是还有一些遗留bug:
-
切换后,状态不能保留。原因是在组件内我读不到全局变量设置的值,如果和page一样采用全局监听方式监听全局变量的改变,然后赋值到当前组件里,监听是监听到了,但是这个this指向的undefind,无法访问组件内的data数据。
-
购物车的红点数量未显示,也是相同原因
-
自动登录失效了
-
身份切换后,点击新的tabbar选项,页面顶部的标题未变,这个问题稍微能好解决点,在main/index.js中切换组件时,加多个当前用的是哪个tabbar进行相应的个改变即可
-
运行结果,如下图所示
setTabBarItem实现
当我带着自定义组件的形式遗留的问题去求助别人时,群友说我这个动态设置tabbar的需求通过微信官方的api就能实现,此刻我的内心拔凉拔凉的,昨天我咋就没发现这个解决方案呢,白折腾了一天,为了不让昨天的折腾白费于是大家就看到这篇文章的分享,也算是给自己的一个安慰。
然而,仔细观察微信提供的api后发现,这只能改变bar的图标和名字,不能改变其跳转的路径。那我要这个api有何用。
我摊牌了,我搞不定了,谁行谁上吧,换一种方式实现吧。
写在最后
- 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注?
- 本文首发于掘金,未经许可禁止转载?
开发Vue项目过程中遇到的问题以及解决方案汇总