小程序自定义 tabBar 如何实现?和原生 tabBar 有什么区别?
简化版
原生 tabBar 通过 app.json 配置,简单稳定但样式和交互受限;自定义 tabBar 通过开启 custom 并实现自定义组件,可以做更复杂的 UI、红点、凸起按钮和业务状态,但需要自己处理选中态、页面同步、性能和兼容问题。
详细版
原生 tabBar:
{
"tabBar": {
"list": [
{ "pagePath": "pages/home/index", "text": "首页" },
{ "pagePath": "pages/mine/index", "text": "我的" }
]
}
}
自定义 tabBar 通常需要:
- 在
app.json的tabBar中开启custom: true。 - 创建
custom-tab-bar组件。 - 在各 tab 页面同步 selected 状态。
- 用
wx.switchTab切换 tab 页面。 - 处理红点、徽标、权限、登录态和样式适配。
面试重点是:自定义 tabBar 灵活性更高,但维护成本也更高。
完整版教学
一、原生 tabBar 是配置驱动
小程序原生 tabBar 通过 app.json 配置。它的优点是简单、性能稳定、平台一致性好,适合大多数常规底部导航。
{
"tabBar": {
"color": "#666666",
"selectedColor": "#07c160",
"list": [
{ "pagePath": "pages/home/index", "text": "首页" },
{ "pagePath": "pages/order/index", "text": "订单" },
{ "pagePath": "pages/mine/index", "text": "我的" }
]
}
}
它的限制也明显:布局结构、动画效果、特殊按钮形态、复杂状态展示都受平台配置约束。如果产品要中间凸起发布按钮、动态会员样式、复杂徽标,就可能需要自定义 tabBar。
面试回答要先表明态度:能用原生就优先原生,只有设计和业务状态确实超出原生能力时再自定义。
二、自定义 tabBar 的核心是 custom-tab-bar 组件
自定义 tabBar 通常先在配置里开启 custom: true,再在项目根目录下创建 custom-tab-bar 组件。平台会在 tab 页面底部渲染这个组件。
{
"tabBar": {
"custom": true,
"list": [
{ "pagePath": "pages/home/index", "text": "首页" },
{ "pagePath": "pages/mine/index", "text": "我的" }
]
}
}
组件内部维护 tab 列表、图标、文字、选中态,并在点击时调用 wx.switchTab。
Component({
data: {
selected: 0,
list: [
{ pagePath: '/pages/home/index', text: '首页' },
{ pagePath: '/pages/mine/index', text: '我的' }
]
},
methods: {
switchTab(e) {
const { path } = e.currentTarget.dataset
wx.switchTab({ url: path })
}
}
})
三、选中态同步是最常见坑
自定义 tabBar 不会自动知道当前页面应该选中哪个 tab,需要页面在 onShow 中主动同步,或通过统一映射计算当前路由。
Page({
onShow() {
const tabBar = this.getTabBar && this.getTabBar()
if (tabBar) {
tabBar.setData({ selected: 1 })
}
}
})
假设有 4 个 tab 页面,如果其中 1 个页面忘记设置 selected,用户从其他页面切回来时,就可能出现“页面是订单,底部却高亮首页”的错位。
进入 tab 页面
-> onShow
-> getTabBar()
-> setData({ selected })
-> 底部选中态更新
工程里通常抽一个 setTabBarSelected(index) 工具函数,避免每个页面写法不同。
四、switchTab、navigateTo 和页面栈区别
tab 页面必须使用 wx.switchTab 切换。navigateTo 不能跳转到 tabBar 页面,switchTab 会关闭其他非 tab 页面并切换到 tab 页面。
| API | 能否跳 tab 页面 | 页面栈影响 | 场景 |
|---|---|---|---|
wx.switchTab | 能 | 切换 tab,关闭非 tab 页面 | 底部导航 |
wx.navigateTo | 不能跳 tab | 入栈新页面 | 普通详情页 |
wx.redirectTo | 不能跳 tab | 替换当前页 | 登录后替换 |
wx.reLaunch | 能 | 清空并打开 | 重置应用状态 |
如果自定义 tabBar 点击时误用 navigateTo,会直接失败或表现异常。面试追问“为什么 tab 页面跳转特殊”,就是考页面栈模型。
五、红点、徽标和登录态要控制数据来源
自定义 tabBar 常用于展示购物车数量、消息红点、会员状态。这里要注意数据来源和更新频率。
App 全局状态 / 请求接口
-> tabBar 组件 setData
-> badge / redDot 更新
假设消息数每 5 秒刷新一次,而 tabBar 每个页面都重新请求,5 个 tab 页面切换一轮就可能触发多次重复请求。更好的方式是集中管理状态,页面显示时按需刷新,或者由消息模块统一推送变化。
数字例子:一个用户 1 分钟切换 tab 20 次,如果每次都请求 3 个徽标接口,就是 60 次请求,完全没必要。可以合并成 1 个 badge 接口,并做短时间缓存。
记忆钩子:自定义 tabBar 的难点不是画出来,而是让选中态、徽标、权限和页面栈一直正确。
六、样式适配和性能要额外关注
自定义 tabBar 要自己处理安全区、不同机型高度、暗色模式、图标加载和点击反馈。原生 tabBar 帮你处理了很多平台细节,自定义后这些成本回到开发者身上。
.tabbar {
padding-bottom: env(safe-area-inset-bottom);
}
如果图标使用远程图片,弱网下可能出现底部图标闪烁。更稳妥的做法是使用本地小图标、雪碧图或字体图标,并控制资源大小。
性能上,tabBar 是高频可见组件,不要在组件里做重计算、大量请求或频繁 setData。选中态变化只需要更新少量字段,不应重设整个列表大对象。
七、常见误区与追问
- 误区:自定义 tabBar 一定比原生好。 自定义更灵活,但选中态、适配、性能和维护成本更高。
- 误区:tab 页面可以用 navigateTo 跳转。 tab 页面应使用
wx.switchTab,页面栈规则不同。 - 误区:自定义 tabBar 会自动同步选中态。 通常需要页面在
onShow中主动设置 selected。 - 追问:为什么红点状态容易乱? 数据来源分散、页面切换重复请求、登录态变化未同步都会导致错乱。
- 追问:什么时候坚持用原生 tabBar? 样式简单、状态少、追求稳定和平台一致性时优先原生。
- 追问:自定义 tabBar 要注意哪些适配? 安全区、机型高度、暗色模式、图标资源、点击反馈和基础库兼容。
八、加强记忆
tabBar 题按“原生稳,自定义活”记:原生配置简单可靠,自定义组件灵活但要自己管 selected、switchTab、badge、登录态、安全区和性能。能把页面栈规则和选中态同步讲清楚,就抓住了这题的关键。