再谈小程序自定义底部导航

小程序自定义tabBar再探索

前言

最近有很多微信开发者朋友在QQ上加我好友,忽然意识到大家对微信自定义底部导航栏需求还是挺大的,故而再次整理下底部导航栏组件开发思路。和之前的文章还是有些区别,并且底部导航栏组件增加新的特性以及一些优化开发体验的骚操作。

技术选型

与之前不同,现在我们有两种方法实现自定义底部导航栏,因为小程序在2.5.0开始支持自定义底部导航栏了戳此处看文档,所以我们现在的可选方案为:

  • 通过HideTabBar接口hack底部导航栏
  • 通过小程序支持配置实现底部导航栏

下面根据两种方式都讲讲

通过HideTabBar接口hack底部导航栏

实现思路

首先我们先了解下微信的路由api和微信的路由机制,微信一共提供了5个路由api:wx.navigateTowx.redirectTowx.switchTabwx.navigateBackwx.reLaunch,具体文档位于https://developers.weixin.qq.com/miniprogram/dev/api/ui-navigate.html

其中我们用哪个呢?很显然wx.switchTab很合我们的口味,因为他的切换效果是没有推入推出动画的,更符合我们的习惯,那使用他的前提是我们需要在app.json文件中配置tabBar属性,而只要一配置了tabBar属性,系统原生的导航栏就出现了,幸好微信有一个隐藏原生导航栏的api:wx.hideTabBar。看到这里相信聪明的同学已经知道了实现思路,就是隐藏掉原生的然后自己去实现一个导航栏贴在最下面。

使用方法

源码地址

本次代码拷贝之后可以直接使用,具体操作如下

  1. 复制components/custom-tab-bar文件夹到你的项目

  2. 在app.json中设置usingComponents对象

  3. 在tab页面的wxml最后追加<custom-tab-bar></custom-tab-bar>

即可观察效果

支持的特性

  • 无需额外配置,甚至不用传参,自动读取app.jsontabBar的配置
  • 使用wx.showTabBarRedDot方法可以设置红点
  • 可以自定义样式,摆脱微信限制(如borderStyle仅支持black/white的限制)
  • 可以定制个性化逻辑
  • 兼容iphoneX,底部自动留空

关键代码介绍

tabBar配置的获取

看过之间博客的同学知道,之前是需要一个额外的route.js文件的,来获取tabBar的配置,原因是小程序js无法读取json文件。随着知(sao)识(cao zuo)掌握增多,发现了更加方便的办法:我们可以通过__wxConfig全局对象的tabBar属性去查看app.json设置。所以我们在组件中直接这样定义:

const fixListConfig = function(item, index) {
  const result = {}; // 使用新对象,类似浅拷贝
  result.pagePath = "/" + item.pagePath.replace(/.html$/g, "");
  result.iconPath = item.iconData
    ? "data:image/png;base64," + item.iconData
    : "/" + item.iconPath;
  result.selectedIconPath = item.selectedIconData
    ? "data:image/png;base64," + item.selectedIconData
    : "/" + item.selectedIconPath;
  result.idx = index;
  result.redDot = false;
  result.text = item.text;
  return result;
};	
const _tabBar = __wxConfig.tabBar;

Component({
  data: {
    activeIdx: -1,
    config: _tabBar,
    list: _tabBar.list.map(fixListConfig)
  },
});

其中list就是我们tab页面列表,tabBar里面还包括了我们定义的一些样式,比如selectColor之类的,因为格式并不是直接拿来就能用,所以我们用map对数据进行了修改和浅拷贝。

此处还有一个坑,部分真机上小程序配置中的图标会变成base64格式的,所以需要做兼容。

上面代码不是难点,关键在于__wxConfig对象,大家可以在微信开发工具控制台执行this.__wxConfig看看里面的内容,相信能对你有所启发。

当前页的判断

相信大家都用过getCurrentPages()这个全局函数,我们也是通过它来获取当前页面的路由然后将其标记为active状态。本着自己的事情自己做的原则,我利用自定义组件的pageLifetimes功能,实现了内部判断当前页,具体代码如下:

pageLifetimes: {
	show() {
	  const pages = getCurrentPages();
	  const page = pages[pages.length - 1];
	  const route = page.__route__;
	  const idx = this.data.list.find(item => item.pagePath === `/${route}`)
	    .idx;
	  if (this.data.activeIdx !== idx) {
	    this.setData({
	      activeIdx: idx
	    });
	  }
	}
}

注意pageLifetimes特性在基础库2.2.3以上支持,目前没什么问题,其实外面传值写死也是ok的,毕竟每个页面是一个单独的组件。

使用css兼容iPhone X(强烈建议了解)

此处一需一行代码

.tab-bar{
	padding-bottom: env(safe-area-inset-bottom);
}

这个方法不光可用于小程序,也可用于移动端,网上有很多介绍他的文章,比如网页适配 iPhoneX,就是这么简单

修改wx接口注入自己的代码逻辑

上面特性中提到了,我们可以使用wx.showTabBarRedDot方法设置我们的redDot,也可以打开上面的小程序微码查看效果。这个是利用Object.defineProperty方法实现的,具体代码可以看custom-tab-bar/extraFun.js文件查看。这块就不再详述了,大家想了解的可以直接阅读源码,如果有疑问可以单独联系我。

通过小程序支持配置实现底部导航栏

现在,我们来聊聊为什么不用小程序支持的配置方法实现底部导航栏。

使用方法

现在我们根据文档进行改造,在app.jsontabBar对象中增加"custom":true,并在根目录下增加custom-tab-bar/index组件。之后的开发就和上面差不多了,我们写组件实现期望的效果。

两者的区别

为了描述方便,我们将之前提到的方法称为方法一,小程序配置的称之为方法二。方法一的组件是在page里面的,方法二的组件是和page并列的关系,如图所示:

这个对我们有什么影响吗,就我目前观察,有几个问题

  • 某些情况下会引起bug,当我们打开一个非tab页面的时候,还是会显示底部导航栏。(最严重问题,会有bug)
  • 无法使用全局定义的样式,哪怕组件激活了addGlobalClass选项之后(不了解addGlobalClass的同学见文档:使组件接受全局样式
  • 需要基础库>=2.5.0,有部分用户无法覆盖。当前版本分布情况

总结

最后还是感谢大家能坚持读完,本人作为一个理科生,文笔太差,有许多想法表达不出来,真是吃了没文化的亏。文中介绍的两种方法,我目前还是只会考虑第一种,以后会一直观望微信配置法,相信当它完善的那天我会毫不犹豫的使用,毕竟跟着标准才能走的更远。如果对本代码或在开发过程中遇到什么问题,欢迎与我交流858582381~

另外,下一步打算分享自定义头部标题栏,不知道大家对此是否期待。

posted @ 2019-02-22 17:34  冻酸奶  阅读(6370)  评论(2编辑  收藏  举报