uni.setTabBarBadge在自定义TabBar下完全无效,因原生角标能力被剥离,须改用组件状态驱动角标显示,并通过全局store或事件机制同步数据,H5平台不支持角标需条件编译降级处理。

uni.setTabBarBadge 在自定义 TabBar 下完全无效
如果你在 pages.json 中设置了 "tabBar": { "custom": true },那么所有原生 TabBar 角标 API(uni.setTabBarBadge、uni.showTabBarRedDot 等)都会静默失效——不是报错,而是调用后毫无反应。这是 uni-app 的硬性限制:自定义 TabBar 意味着你接管了整个底部导航的渲染逻辑,原生角标能力自动剥离。
验证方式很简单:console.log(uni.getSystemInfoSync().platform) + 检查 pages.json 是否含 "custom": true。只要两者同时存在,就别再尝试调用原生角标 API。
- 必须通过组件内部状态驱动角标显示(如
v-if控制<view class="badge">99+</view>) - 角标数据不能依赖页面生命周期直接写死,需统一管理(推荐 Vuex/Pinia 或
uni.$emit+ 全局监听) - 自定义 TabBar 组件里不要用
uni.switchTab后再手动触发onLoad,这会破坏路由栈一致性
角标状态该存在哪?全局 store 还是页面 data?
角标数字本质是跨页面共享的状态:比如「消息页」有 5 条未读,「首页」切换过去时也得同步显示这个数字。放在单个页面 data 里会导致状态割裂,切页即丢失;而放在全局 store(如 store/tabbar.js)中,能确保任意页面修改后,自定义 TabBar 组件立刻响应。
示例结构(Vuex 风格):
export default {
state: {
tabbarList: [
{ pagePath: '/pages/home/index', text: '首页', count: 0 },
{ pagePath: '/pages/msg/index', text: '消息', count: 5, isDot: false }
]
}
}-
count为 0 时隐藏数字角标,>0 时显示(超过 99 显示"99+") -
isDot单独控制红点(避免和数字共存造成视觉混乱) - 修改时必须用
commit或dispatch,不可直接改state.tabbarList[1].count = 3(响应式会失效)
什么时候更新角标?onShow 不可靠,得靠事件驱动
很多开发者习惯在目标页面(如消息页)的 onShow 里请求未读数并更新 store,但问题在于:用户可能从非 TabBar 页面(如详情页)返回,此时 onShow 虽然触发,但自定义 TabBar 组件未必已挂载或已订阅到最新状态。
更稳妥的方式是「事件驱动 + 延迟同步」:
- 在 App.vue 的
onLaunch和onShow中用uni.$on('updateTabbarBadge')监听全局事件 - 任何页面拿到新未读数后,立即
uni.$emit('updateTabbarBadge', { index: 1, count: 7 }) - 自定义 TabBar 组件内用
computed或watch监听 store 变化,触发视图更新 - 避免在
onHide中取消监听——否则切到非 TabBar 页面再回来,事件就断了
H5 平台要单独处理,别指望它支持角标
即使你没开自定义 TabBar,uni.setTabBarBadge 在 H5 平台也完全不生效,且不报错、无提示。这不是 bug,是 uni-app 官方明确标注的限制(见 CSDN 问答平台支持表)。所以你的自定义 TabBar 组件里,必须加条件编译:
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
<view v-if="item.count && item.count > 0" class="badge">
{{ item.count > 99 ? '99+' : item.count }}
</view>
<!-- #endif -->如果业务强依赖角标提示,H5 端建议降级为顶部通知栏 banner 或页面内悬浮提示,而不是强行模拟底部角标——样式错位、点击穿透、z-index 冲突等问题会集中爆发。
最易被忽略的一点:自定义 TabBar 的角标位置必须用绝对定位 + 手动计算 left/top,不同机型 tabBar 高度不同(尤其 iOS 安全区),别写死 top: -8px 这种值。


















