uni-app无法直接用custom+tabBar实现凸起效果,因custom:true后原生tabBar被隐藏,需手动用fixed定位+安全区适配+绝对定位凸起按钮,并通过uni.onTabItemTap统一管理状态。

uni-app 自定义 tabBar 为什么不能直接用 custom + tabBar 配置实现凸起效果
因为 uni-app 官方 tabBar 配置只支持「平铺式」图标+文字,custom: true 后会完全接管 tabBar 渲染,但此时原生 tabBar 区域被隐藏,你得自己手写一个悬浮层——而这个悬浮层默认在页面底部,不会自动「凸起」或「居中上浮」,更不响应原生点击区域逻辑。
常见错误现象:custom: true 后写了个圆角矩形 div,发现它被页面内容遮挡、无法固定到底部、点击区域偏移、iOS 上有安全区切割。
- 必须用
position: fixed+bottom: 0锚定,但需手动处理 iPhone X+ 的env(safe-area-inset-bottom) - 凸起部分(如中间按钮)不能靠 margin/padding 撑开,得用绝对定位脱离文档流
- 原生 tabBar 高度(通常 50px)和自定义层高度不一致时,
page.json中的tabBar配置仍会保留占位,导致页面底部多出空白
如何用 uni-app 实现真正可点击的凸起 tabBar 中间按钮
核心是:用固定定位模拟 tabBar,把中间按钮单独抽离为绝对定位元素,覆盖在 tabBar 水平布局之上,并手动绑定点击事件和状态同步。
使用场景:需要中间按钮比两侧高、带阴影、点击反馈明显,且要保持其他 tab 切换正常、状态高亮准确。
- 在
pages.json中设"tabBar": { "custom": true },并删掉所有list配置(否则会残留原生 tabBar) - 在每个 tab 页面的根组件中引入统一的
CustomTabBar.vue,用v-if="$scope && $scope.$parent && $scope.$parent.$scope"确保只在 tab 页面生效 - 凸起按钮的
z-index必须高于左右 tab 项(例如z-index: 10),且宽高建议用vh/vw或固定 rpx 值,避免缩放失真 - 点击中间按钮时,不能只跳转页面,还得调用
uni.switchTab({ url: '/pages/xxx' })并手动触发onTabItemTap生命周期(否则 onTabItemTap 不执行)
示例关键结构:
<view class="tabbar"> <view class="tab-item" @click="switchTab(0)"></view> <view class="tab-item" @click="switchTab(1)"></view> <view class="tab-raise" @click="onRaiseClick"></view> <view class="tab-item" @click="switchTab(2)"></view> <view class="tab-item" @click="switchTab(3)"></view> </view>
uni-app 凸起 tabBar 在 iOS 和 Android 上的兼容性差异
不是样式写错,而是底层渲染机制不同:iOS 原生 tabBar 有严格的安全区约束,Android 则依赖 WebView 底层对 fixed 的支持程度。
性能影响:自定义 tabBar 全量重绘,每次 tab 切换都会触发整个组件更新;若中间按钮带动画,低端 Android 机可能出现卡顿。
- iOS 必须加
padding-bottom: env(safe-area-inset-bottom)到外层容器,否则凸起按钮会被刘海遮住下半截 - Android 8.0 以下 WebView 对
position: fixed支持差,建议 fallback 为position: absolute+ 监听scroll手动修正位置(不推荐,仅保底) - 凸起按钮的点击热区在 iOS 上默认偏小,需用
padding扩展,但不能用transform: scale(),否则点击坐标映射错乱 - App 平台下,
uni.getSystemInfoSync().platform === 'ios'可用于条件样式,但 H5 平台无此问题,无需判断
为什么不要在 onLoad 里初始化自定义 tabBar 状态
因为 onLoad 触发时,页面 DOM 可能尚未挂载完毕,this.$nextTick 也不可靠——尤其是 tab 页面被缓存后再次激活,onLoad 根本不执行。
容易踩的坑:用 ref 获取凸起按钮 DOM 节点失败、current 状态没同步、首次进入时高亮错位。
- 状态管理必须放在
onShow或onTabItemTap中,后者只在真点击时触发,前者确保每次显示都校准 - 不要用
data初始化 activeIndex,改用computed从getCurrentPages()的路径推导,更稳定 - 如果用了 Pinia/Vuex,记得在 tabBar 组件中
watch路由变化,而不是依赖页面生命周期
最简健壮写法是:监听 uni.onTabItemTap 全局回调,统一更新状态,再通知所有 tab 页面重新渲染。
凸起本身不难,难的是让所有平台都“像原生一样点哪哪响应”,这点常被忽略——尤其是测试时只看点击动效,不验证 tab 切换后页面数据是否刷新、返回时状态是否还原。










