uniapp自定义tabBar

发布于:2023-01-14 ⋅ 阅读:(1242) ⋅ 点赞:(1)

目录

uniapp自定义tabBar方案

一、pages.json文件中添加tarBar

二、把原生的tabBar隐藏起来

三、自定义一个tabBar组件

四、全局引用组件

五、路由跳转

六、完整代码

补充1:自定义switchTab跳转方法

补充2:在app中$route找不到的问题


uniapp自定义tabBar方案

该方案,可以在切换tabBar的时候,路由也跟着变化,方便平时进行页面测试使用,不过有个缺点,第一次切换的时候会出现闪烁现象。

解决闪烁的问题:

1、可以把tabBar和页面组件都放在单页中用v-if判断,进行切换,单页切换的缺点是不能进行路由跳转(不能路由跳转)

<template>
	<view>
		<index></index>
		<my></my>
		<team></team>
		<promotion></promotion>
	   <tab-bar></tab-bar>
	</view>
	</view>
</template>

 2、把组件注入App.vue中,实现全局引用,但是uniapp不建议在App.vue中使用页面组件,也不要使用,不然出现各种问题,uniapp不能像vue那样在App.vue使用router-view实现选项卡功能(不推荐)

3、 所以要同时实现首次不闪烁,而且可以进行路由切换的功能,目前不知道如何处理。(未知)

(方案还有需要完善的地方,这里仅供大家参考)

一、pages.json文件中添加tarBar

因为需要用到tabBar跳转,因此可以往原来的tabBar.list中多添加几个,用于做判断是否tabBar切换(这里可以只添加路径,icon和text可以不需要)

二、把原生的tabBar隐藏起来

在App.vue中把原生的tabBar先隐藏起来

onLaunch: function() {
	console.log('App Launch')
	uni.hideTabBar()
},

三、自定义一个tabBar组件

在components中新建一个tabBar.vue的页面,用来封装组件

HTML代码,这里循环遍历自己定义的tabBar.list(可根据需求来定义)

<view v-if="showselected">
   <view class="tabbar">
			<view class="navigator">
				<view class="navigator-item" v-for="(item,index) in tabBar.list" :key="item.pagePath"
					@click="switchTab(index,item)">
						<img :src="item.iconPath" class="icon" v-if="selectedIndex !== index">
						<img :src="item.selectedIconPath" class="icon" v-else>
						<text :class="['item-text',{'text-active':selectedIndex === index}]">{{item.text}}</text>
				</view>
			</view>
		</view>
  </view>

data里面定义变量和循环列表,list即是自己定义的tabBar,和pages.json的tabBar写法一样

data() {
			return {
				selectedIndex: 0, // 标记
				showselected: false, // 是否在页面使用tarBar
				tabBar: {
					list: [{
							"pagePath": "pages/index/index",
							"iconPath": "/static/tabIcon/icon1.png",
							"selectedIconPath": "/static/tabIcon/icon2.png",
							"text": "首页"
						}, {
							"pagePath": "pages/team/team",
							"iconPath": "/static/tabIcon/icon3.png",
							"selectedIconPath": "/static/tabIcon/icon4.png",
							"text": "团队"
						}, {
							"pagePath": "pages/promotion/promotion",
							"iconPath": "/static/tabIcon/icon5.png",
							"selectedIconPath": "/static/tabIcon/icon6.png",
							"text": "推广圈"
						},
						{
							"pagePath": "pages/my/my",
							"iconPath": "/static/tabIcon/icon7.png",
							"selectedIconPath": "/static/tabIcon/icon8.png",
							"text": "我的"
						}
					]
				},
			}
		},

如果需要根据不同用户来渲染不同tabBar,可用vuex来保存tabBar的list列表

const user_1 = [{
		"pagePath": "../pages/test/me",
		"iconPath": "../static/xxx.png",
		"selectedIconPath": "../static/xxx.png",
		"text": "me"
	}, {
		"pagePath": "../pages/test/you",
		"iconPath": "../static/xxx.png",
		"selectedIconPath": "../static/xxx.png",
		"text": "you"
	}, {
		"pagePath": "../pages/test/other",
		"iconPath": "../static/xxx.png",
		"selectedIconPath": "../static/xxx.png",
		"text": "other"
	},
	
]
const user_2 = [{
		"pagePath": "../pages/test/our",
		"iconPath": "../static/xxx.png",
		"selectedIconPath": "../static/xxx.png",
		"text": "our"
	}, {
		"pagePath": "../pages/test/his",
		"iconPath": "../static/xxx.png",
		"selectedIconPath": "../static/xxx.png",
		"text": "his"
	}, 
]
export default {
	user_1,
	user_1
}

四、全局引用组件

往main.js注入组件

// main.js
import tabBar from 'components/tab-bar/tabBar.vue'
Vue.component('tab-bar',tabBar) //挂载

在每一个页面中引入

<template>
	<view>
		<view class="">我是首页</view>
		<tab-bar></tab-bar>
	</view>
</template>
<template>
	<view>
		<view class="">我是推广圈页</view>
		<tab-bar></tab-bar>
	</view>
</template>
<template>
	<view>
		<view class="">我是团队页</view>
		<tab-bar></tab-bar>
	</view>
</template>

五、路由跳转

tabBar定义成了组件,因此需要一个全局变量来判断它的切换和路由的跳转。

首先,进入到uniapp的官方tabBar源码中,可以看到这样一段代码,下面的这段代码,就是路由跳转的依据,把他复制到自己的组件中,进行改造。该监听方式只能在uniapp中有效,在app中无法监听,详情往下看【补充2】

 watch: {
    $route: {
      immediate: true,
      handler (to) {
        if (to.meta.isTabBar) { // 判断是否属于tarBar
          // this.__path__ = to.path
          // 判断路由的路径和tarBar.list的pagePath是否相同,来确定一个全局的index
          const index = this.list.findIndex(item => to.meta.pagePath === item.pagePath)
          if (index > -1) {
            this.selectedIndex = index // 标记是第几个tarBar
          }
        }
      }
    }
  },

 定义跳转的方法,这里用的是uni.switchTab进行跳转,不然url不会发生变化。注释掉的代码是官方的代码,这里是直接复制过来自己用。注:后续要使用switchTab进行跳转的话,就需要到自定义的方法,毕竟已经自定义了tabBar,跳转方式也需要自定义。自定义跳转方法请看文章后面的【补充1】

methods: {
			switchTab(index, item) {
				let url = '/' + item.pagePath
				let pagePath = url
				const detail = {
				  index,
				  pagePath
				}
				if (this.$route.path !== url) {
				  // this.__path__ = this.$route.path
				  uni.switchTab({
				    from: 'tabBar',
				    url,
				    detail
				  })
				} else {
				  // UniServiceJSBridge.emit('onTabItemTap', detail)
				}
				this.selectedIndex = index
				// this.$emit('switchTab', detail)
			}
		},

六、完整代码

<template>
		<view class="tabbar">
			<view class="navigator">
				<view class="navigator-item" v-for="(item,index) in tabBar.list" :key="item.pagePath"
					@click="switchTab(index,item)">
						<img :src="item.iconPath" class="icon" v-if="selectedIndex !== index">
						<img :src="item.selectedIconPath" class="icon" v-else>
						<text :class="['item-text',{'text-active':selectedIndex === index}]">{{item.text}}</text>
				</view>
			</view>
		</view>
	</view>

</template>

<script>
	export default {
		watch: {
			$route: {
				immediate: true,
				handler(to) {
					if (to.meta.isTabBar) {
						// this.__path__ = this.$route.path
						this.showselected = true
						const index = this.tabBar.list.findIndex(item => to.meta.pagePath === item.pagePath)
						if (index > -1) {
							this.selectedIndex = index
						}
					}
				}
			}
		},
		data() {
			return {
				selectedIndex: 0,
				showselected: false,
				tabBar: {
					list: [{
							"pagePath": "pages/index/index",
							"iconPath": "/static/tabIcon/icon1.png",
							"selectedIconPath": "/static/tabIcon/icon2.png",
							"text": "首页"
						}, {
							"pagePath": "pages/team/team",
							"iconPath": "/static/tabIcon/icon3.png",
							"selectedIconPath": "/static/tabIcon/icon4.png",
							"text": "团队"
						}, {
							"pagePath": "pages/promotion/promotion",
							"iconPath": "/static/tabIcon/icon5.png",
							"selectedIconPath": "/static/tabIcon/icon6.png",
							"text": "推广圈"
						},
						{
							"pagePath": "pages/my/my",
							"iconPath": "/static/tabIcon/icon7.png",
							"selectedIconPath": "/static/tabIcon/icon8.png",
							"text": "我的"
						}
					]
				},
			}
		},
		// beforeCreate() {
		// 	this.__path__ = this.$route.path
		// },
		methods: {
			switchTab(index, item) {
				let url = '/' + item.pagePath
				let pagePath = url
				const detail = {
				  index,
				  pagePath
				}
				if (this.$route.path !== url) {
				  // this.__path__ = this.$route.path
				  uni.switchTab({
				    from: 'tabBar',
				    url,
				    detail
				  })
				} else {
				  // UniServiceJSBridge.emit('onTabItemTap', detail)
				}
				this.selectedIndex = index
				// this.$emit('switchTab', detail)
			}
		},
	}
</script>

<style>
	.tabbar {
		position: fixed;
		bottom: 0;
		left: 0;
		width: 100%;
		height: 100rpx;
		z-index: 999;
		background: #F5F5F5;
		border-top: 2rpx solid #eee;
	}

	.navigator {
		display: flex;
		justify-content: space-between;
		width: 85%;
		margin: 0 auto;
		padding: 20rpx;
	}

	.navigator-item {
		display: flex;
		align-items: center;
		flex-direction: column;
		width: 50rpx;
		height: 100%;
	}

	.item-text {
		margin-top: 6rpx;
		color: #777E86;
		font-size: 24rpx;
	}

	.text-active {
		color: #2E92FD !important;
	}

	.icon {
		width: 20px;
		height: 20px;
	}
</style>

补充1:自定义switchTab跳转方法

由于在子组件中已经存在switchTab方法,现只需做一下调整即可,先调整一下switchTab方法,如下:

            /*   
			// 调整前
			methods: {
				switchTab(index, item) {
					let url = '/' + item.pagePath
					let pagePath = url
					const detail = {
						index,
						pagePath
					}
					if (this.$route.path !== url) {
						// this.__path__ = this.$route.path
						uni.switchTab({
							from: 'tabBar',
							url,
							detail
						})
					} else {
						// UniServiceJSBridge.emit('onTabItemTap', detail)
					}
					this.selectedIndex = index
					// this.$emit('switchTab', detail)
				}
			}, */
			// 调整后,1、调换入参顺序,方便传值,2、循环判断一下index,
			switchTab(item, index) {
				let url = '/' + item.pagePath
				let pagePath = url
				const detail = {
					index,
					pagePath
				}
				if (this.$route.path !== url) {
					// this.__path__ = this.$route.path
					let s = uni.switchTab({
						url,
						detail
					})
				} else {
					// UniServiceJSBridge.emit('onTabItemTap', detail)
				}
				this.tabBar.list.forEach((v, i) => {
					if (item.pagePath === v.pagePath) {
						this.selectedIndex = i
				   }
				})
				// this.$emit('switchTab', detail)
			}

这里的index和item也要调换一下位置 ,@click="switchTab(item,index)"

父组件中使用ref="tabBar"来获取子组件 

<template>
	<view>
		<view class="">我是首页</view>
		<button @click="toMY">跳转</button>
		<tab-bar ref="tabBar"></tab-bar>
	</view>
</template>

通过方法来触发子组件方法即可, 对象中的pagePath是与tabBar.list数组里面的pagePath是对应的。

toMY() {
	this.$refs.tabBar.switchTab({
	   pagePath:'pages/team/team'
	})
},

补充2:在app中$route找不到的问题

打包到小程序之后,$route会像下面图片那样显示undefined,即在小程序中,是没有像$route这样的路由方式。那么,就不能用这样的方式,来监听index的变化。

 如何解决?

利用uni.setStorageSync()全局保存一下点击tabBar的index即可,进入到tabBar.vue文件。

1、重写switchTab()方法

2、注释掉$route的监听。

3、调整data中定义的变量selectedIndex

<script>
	export default {
		// watch: {
		// 	$route: {
		// 		immediate: true,
		// 		handler(to) {
		// 			if (to && to.meta.isTabBar) {
		// 				// this.__path__ = this.$route.path
		// 				// this.showselected = true
		// 				const index = this.tabBar.list.findIndex(item => to.meta.pagePath === item.pagePath)
		// 				if (index > -1) {
		// 					this.selectedIndex = index
		// 				}
		// 			}
		// 		}
		// 	}
		// },
		data() {
			return {
				selectedIndex: uni.getStorageSync('selectedIndex') || 0,
			}
		},
		methods: {
			switchTab(item, index) {
				let url = '/' + item.pagePath
				let pagePath = url
				uni.switchTab({url})
				this.tabBar.list.forEach((v, i) => {
					if (item.pagePath === v.pagePath) {
						uni.setStorageSync('selectedIndex', index);
					}
				})
			}
		},
	}
</script>

最后进入App.vue文件中,清除一下selectedIndex的缓存,不在tabBar.vue的created生命周期清除selectedIndex的原因是:只希望,每次重新进入程序后重置,tabBar的高亮重新回到第一个,而不是每次进入页面的重置,而且每次进入页面重置,是有问题。

<script>
	export default {
		
		onLaunch: function() {
			uni.removeStorageSync('selectedIndex'); // 清除缓存
			uni.hideTabBar()
		},
	}
</script>