首页 / 资讯中心 / 文章详情

uniapp App端tabBar中间凸起按钮实现:cover-view与自定义组件方案对比

uniapp App端tabBar中间凸起按钮实现:cover-view与自定义组件方案对比 ★ FEATURED ARTICLE
1. 方案选型与底层逻辑做uniapp的App端开发遇到tabBar中间图标凸起这种需求是迟早的事。电商、社区、工具类应用基本都想把“发布”“扫一扫”或者“拍照”这种最高频的动作放在中间做成一个突出的圆形按钮视觉上更抓眼球交互上也方便拇指点按。但这个需求并不像想象中那么好实现。官方tabBar本来只是一个平铺的导航条所有图标都是均匀排列没有给你预留“凸起”这个位置。很多人在网上搜了一堆方案拿过来改了改结果不是iOS上正常、Android上错位就是真机调试没问题、打包后图标直接不见了。问题出在没有理解清楚方案的底层逻辑。先说结论App端要实现tabBar图标凸起本质上只有两条路。一条是继续使用官方tabBar把某个tab的图标“伪装”成凸起效果另一条是彻底抛弃官方tabBar用自定义组件完全接管底部导航的渲染。这两条路各有各的道理也有各自的坑选错了后续很麻烦。官方tabBar的好处是稳定、原生渲染、性能和切换动画都不用你操心应用启动时底部导航就已经就位了。隐患在于它能提供的定制能力极其有限市面上流传的“使用cover-view实现凸起”的方案本质上是把凸起按钮当成一个悬浮物盖在原生tabBar上面官方导航本身并没有变。这种方案最大的问题是cover-view的排版能力弱样式稍微复杂一点就容易出兼容性问题尤其是Android端的渲染差异非常大。自定义tabBar的思路则完全不同。它相当于你用一个普通组件在页面底部自己画一个导航栏出来点击哪个tab就切换到对应页面同时管理选中态、角标、凸起样式。表面上看起来多写了不少代码实际上因为每个像素都是自己控制的所以凸起效果想怎么折腾都行。代价就是原生的切换动画没有了路由切换需要你手动处理部分机型上可能因为渲染时机问题出现短暂白屏。这两类方案的适用场景也不一样。如果你的项目只改一个中间按钮其它tab都保持原生行为且希望尽量少改代码那官方tabBar加悬浮层会更合适。如果你对底部导航的视觉要求较高比如要毛玻璃背景、红点动画、动态字体大小甚至做深色模式适配自定义tabBar基本就是唯一的选择。我见过不少开发者一上来就直奔自定义方案结果页面多了几个hooks、弹窗、下拉刷新之后底部导航的状态同步开始出问题越改越乱。反过来也有团队坚持用官方tabBar加悬浮层做到后来发现角标位置、安全区适配、路由联动都要额外写兼容代码代码量一点不比自定义方案少反而更绕。所以选方案之前先想清楚一件事你对底部导航的定制需求只是“凸起”这一个点还是说未来还可能有更多的视觉、交互上的改动如果是前者走官方tabBar路线性价比最高如果是后者别犹豫直接上自定义tabBar越早切换成本越低。另外题目特别标注了“App端”这一点很关键。因为同样的问题在H5端和小程序端的解法完全不同。H5端css的position: fixed、z-index那一套怎么玩都不会有太大问题小程序的cover-view在iOS和Android上的表现差异也有成熟的兜底方案。但App端的渲染层逻辑不一样尤其是用nvue渲染的时候很多在H5上看起来没问题的写法到App上直接失效。2. 方案一官方tabBar 悬浮凸起按钮的完整拆解2.1 为什么选择cover-view作为悬浮载体方案一的整体结构非常简单官方tabBar照常配置四个tab中间的tab不做图标或者只放一个透明占位。然后我在页面上叠加一个cover-view组件把凸起的按钮渲染在tabBar中间位置的偏上方点击它的时候通过uni.switchTab跳转到对应页面。这里最关键的技术选型是悬浮层用什么组件承载。很多第一次做这个需求的人会想当然地用view然后设置position: fixedbottom: 30pxleft: 50%再加上transform: translateX(-50%)这套写法在H5端似乎一切正常但放到App端就有问题。问题出在原生tabBar和webview渲染的层级关系上。App端的官方tabBar是原生层组件它永远在最上层普通view组件不管设置多大的z-index都不可能盖在原生tabBar上面。这就是为什么你在H5上调试得好好的一跑到真机上凸起按钮就莫名其妙地藏到tabBar底下去了。解决这个层级问题有两个思路一个是用plus自带的subNVue原生子窗体另一个就是用cover-view。cover-view是专门设计来覆盖在原生组件之上的它能盖住map、video这些原生组件同样也能盖住原生tabBar。我自己的实践也确认了这一点把凸起按钮的样式放在cover-view上它能稳定地悬浮在原生tabBar之上层级没问题。但cover-view也有明显的短板——它的样式支持范围很小只支持有限的css属性border-radius、box-shadow这些勉强能用但如果你要做复杂的渐变色、毛玻璃模糊、头像圆环加旋转动画之类的效果cover-view基本就撑不住了。所以这个方案最适合的场景是凸起按钮的样式本身不算复杂主要靠一张png图片或者iconfont字体。顺便插一句也有人用subNVue来放这个凸起按钮优点是可以写完整的vue页面和css视觉效果上限高很多但subNVue的配置和通信成本不小要用uni.openSubNVue、uni.postMessage来管理它的创建和通信对于一个按钮来说我觉得有点重了。注意All of this的前提是你的底部导航确实用的是原生tabBar。如果你用的是自定义tabBar那方案一就完全不适用你已经在管渲染层了直接用view就好不需要再费劲去搞层级的绕行。2.2 完整实现从pages.json配置到样式细节方案一的落地按照以下步骤操作。pages.json里tabBar的list配置保持四个tab中间那个tab正常配置pagePath和text但是iconPath和selectedIconPath都指向一个透明图片。这里需要两个1x1像素的透明png放不放在static目录无所谓记得路径别填错就行。{ pages: [ { path: pages/index/index }, { path: pages/publish/publish }, { path: pages/message/message }, { path: pages/mine/mine } ], tabBar: { color: #999999, selectedColor: #333333, backgroundColor: #ffffff, borderStyle: black, list: [ { pagePath: pages/index/index, text: 首页, iconPath: static/tab-home.png, selectedIconPath: static/tab-home-active.png }, { pagePath: pages/publish/publish, text: , iconPath: static/tab-transparent.png, selectedIconPath: static/tab-transparent.png }, { pagePath: pages/message/message, text: 消息, iconPath: static/tab-msg.png, selectedIconPath: static/tab-msg-active.png }, { pagePath: pages/mine/mine, text: 我的, iconPath: static/tab-mine.png, selectedIconPath: static/tab-mine-active.png } ] } }注意中间那个text我留空了因为官方tabBar的文字和凸起图标放在一起会出现两条文字或者位置错乱的问题干脆就不显示文字视觉上更干净。当然这也有代价在官方tabBar的布局里中间tab的体积仍然存在着点击它的空白区域也会触发页面切换这一点后面我会讲怎么处理。然后在你需要在底部显示凸起按钮的页面上也就是tabBar里的那几个页面分别引入凸起按钮的组件。不想每个页面都手动引入的话可以写一个通用混入或者全局组件注册在main.js里用Vue.component注册一次这样所有页面都能用。凸起按钮的核心实现长这样封装成一个组件template cover-view classtabbar-center-btn taphandleTap cover-view classtabbar-center-btn__inner cover-image classtabbar-center-btn__icon src/static/ic-publish.png / /cover-view /cover-view /template script export default { name: CenterBtn, methods: { handleTap() { uni.switchTab({ url: /pages/publish/publish }) } } } /script style langscss scoped .tabbar-center-btn { position: fixed; bottom: calc(20px env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%); width: 104px; height: 104px; border-radius: 50%; background: linear-gradient(135deg, #ff9a56, #ff6a00); box-shadow: 0 8px 20px rgba(255, 106, 0, 0.35); display: flex; align-items: center; justify-content: center; z-index: 999; } .tabbar-center-btn__inner { width: 88px; height: 88px; border-radius: 50%; background: #ffffff; display: flex; align-items: center; justify-content: center; } .tabbar-center-btn__icon { width: 46px; height: 46px; } /style几个关键的数值需要仔细斟酌。底部偏移量bottom的值官方tabBar的高度一般是50px部分平台是56px或60px加上安全区高度。凸起按钮要露出一半左右在tabBar之上所以底部的距离要稍微大于tabBar高度的一半我给的是20px基准加安全区。这个值不是固定的要结合你的按钮高度、希望露出的比例去做微调建议真机上反复调试。如果你要按基准值计算可以这样想官方tabBar的高度在App端一般约50px安全区高度在iPhone X及以上的机型是34px按钮高度若是104px要让按钮上半部分露在外面bottom应该等于安全区高度 按钮露出高度。按钮露出高度通常取按钮高度的40%-50%比如50px那bottom就是84px。加上半个按钮的高度52px整体覆盖范围就能算出tabBar上大约会盖到多少。像上面这套配置按钮顶部实际位于tabBar顶端上方约34px处视觉上已经凸起得很明显了。我实际测试下来这个方案在iOS和Android上表现基本稳定。但cover-view对box-shadow的支持在不同平台上有差异Android上偶尔会出现阴影被裁切或者不显示的情况使用时可以提前准备一套无阴影的退化样式。2.3 非tabBar页面的显示问题方案一还有一个容易踩坑的点凸起按钮并不会全局自动出现。它的载体是cover-view你写在哪个页面它就只在哪个页面渲染。tabBar页面有首页、消息、我的加上中间那个publish一共四个你在首页写了这个按钮切到消息页消息页是另外独立的页面不会自动带上这个按钮。解决方案有两种。一种是每个tab页面都手动引入CenterBtn组件缺点是要改的地方多而且每个页面都会有这份代码。另一种是把CenterBtn做成全局组件在main.js里注册// main.js import CenterBtn from /components/CenterBtn.vue Vue.component(CenterBtn, CenterBtn)然后在每个tab页面放一行template view !-- 页面内容 -- CenterBtn / /view /template如果你用的是uniapp的easycom规范甚至不需要手动引入和注册组件放到components/CenterBtn.vue页面模板里直接用 就会自动按需引入这是最低成本的方案。这是方案一最别扭的地方它不是一个真正意义上的“全局”渲染而是靠每个页面自己去挂载一次。好处是这样你能针对每个页面调整按钮的样式比如首页淡一点消息页深一点但坏处是很容易漏某个新页面忘记挂载那个页面上就没有凸起按钮。另一个要注意的是页面生命周期。cover-view是有渲染层概念的偶尔在页面刚跳转过来时会晚一帧出现看起来像是闪了一下影响不大但如果你的设计对视觉细节要求苛刻还是需要心里有数。实操心得真正生产环境里我反而推荐在publish页和首页这两个最高频场景里挂载消息和我的页面如果产品没强制要求可以不用挂。这样少两个页面的维护负担而且这两个页面用户本来就很少盯着底部看。3. 方案二完全自定义tabBar的组件化实现3.1 自定义tabBar的整体设计与路由联动方案二走的是彻底接管的路子。官方tabBar在pages.json里直接不配置或者只保留tabBar这个key的占位配置页面的底部导航完全由一个自定义组件来渲染。这个方案的架构核心是分清两个职责视觉层和状态层。视觉层就是那个自定义组件它接收当前的选中索引根据索引渲染对应的图标、文字、选中态以及中间的凸起按钮。状态层则是路由跳转用户点击某项时调用uni.switchTab或uni.reLaunch去切换页面同时把当前选中的tab索引传给组件让组件更新高亮状态。注意既然你不用官方tabBar了页面之间就不能用uni.navigateTo去跳转tab页面。uni.navigateTo会触发页面栈叠加跳来跳去返回逻辑会乱。你要用uni.switchTab或uni.reLaunch这两个方法才能保证tab页面的切换逻辑正确。路由联动的关键是在每个tab页面的onShow生命周期里更新当前选中项。因为如果用户是通过分享卡片、扫码、系统通知等方式进入某个页面你要保证底部导航的高亮状态和当前页面保持一致。架构上我的做法是在组件内部保存一个activeIndex变量页面每次onShow的时候去更新这个值。如果项目用的是Vuex或pinia也可以把activeIndex放进store里组件通过计算属性读取。数据放store的好处是将来如果需要在某个页面让底部导航直接切到指定tab只需要改store里的值组件会自动响应。3.2 页面结构改造pages.json与新页面写法先看pages.json怎么改。最简单的方式是把官方tabBar整个删掉然后在每个tab页面的config里加一个属性{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 首页, disableScroll: true } }, { path: pages/publish/publish, style: { navigationBarTitleText: 发布, disableScroll: true } }, { path: pages/message/message, style: { navigationBarTitleText: 消息, disableScroll: true } }, { path: pages/mine/mine, style: { navigationBarTitleText: 我的, disableScroll: true } } ], globalStyle: { navigationBarTextStyle: black, navigationBarTitleText: 正在加载, navigationBarBackgroundColor: #ffffff, backgroundColor: #f5f5f5 } }你没看错就是完全不配置tabBar字段。官方tabBar的特性如初始化时自动定位到指定tab、页面栈管理、原生的切换动画你统统用不了。作为等价方案你需要在App启动时默认展示第一个tab页面页面之间的切换由自定义tabBar的点击事件接管。页面写法上每个tab页面在自己的结构里引入自定义tabBar组件放在页面底部template view classpage-wrapper !-- 页面主要内容 -- view classpage-content ... /view CustomTabBar :active-indexcurrentTab / /view /template script export default { data() { return { currentTab: 0 } }, onShow() { this.currentTab 0 } } /script每个页面里currentTab的值不一样首页是0发布页是1消息页是2我的页是3。onShow里同步更新就能够保证组件的高亮状态渲染正确。值得多提一句的是因为要预留tabBar的高度给自定义导航栏页面的主要滚动区域需要用padding-bottom或者margin-bottom把内容撑上去。我习惯的做法是在页面根view上设置padding-bottom: calc(140px env(safe-area-inset-bottom))这样内容就不会被底部的导航遮挡避免滚动时最后几行文字被盖住。3.3 凸起按钮的实现圆环、阴影与高清图标适配自定义tabBar组件的实现核心在于view可以随便布局凸起按钮因此变得很简单。我把中间tab的容器做成一个相对定位的元素里面的凸起按钮用绝对定位往上偏移视觉上就是从tabBar上长出来的。按钮组件内部使用view搭建加阴影、圆角、边框甚至用background-image做渐变背景几乎没有任何限制。template view classcustom-tabbar view classtabbar-item v-for(item, index) in tabbarList :keyindex taphandleSwitchTab(index) image classtabbar-item__icon :srcitem.iconPath / text classtabbar-item__text :class{ active: activeIndex index }{{ item.text }}/text /view view classtabbar-center-container view classtabbar-center-btn taphandleSwitchTab(1) image classtabbar-center-btn__icon src/static/ic-publish-active.png / /view text classtabbar-center-text发布/text /view /view /template script export default { name: CustomTabBar, props: { activeIndex: { type: Number, default: 0 } }, data() { return { tabbarList: [ { iconPath: /static/tab-home.png, text: 首页 }, { iconPath: /static/tab-msg.png, text: 消息 }, { iconPath: /static/tab-mine.png, text: 我的 } ] } }, methods: { handleSwitchTab(index) { if (index 0) { uni.switchTab({ url: /pages/index/index }) } else if (index 1) { // 发布是特殊页 uni.navigateTo({ url: /pages/publish/publish }) } else if (index 2) { uni.switchTab({ url: /pages/message/message }) } else if (index 3) { uni.switchTab({ url: /pages/mine/mine }) } } } } /script等等这里我换了一招。中间那个tab如果和官方tabBar一样用switchTab来控制页面切换会有个问题发布页如果是一个完整页面用户从发布页退出时不一定想回到之前的tab而且发布本来就是偏模态的操作。所以这里凸起按钮我用了navigateTo将发布页做成一个普通页面用户填写完内容可以直接返回这样比tab切换的体验更加自然。这个决策因人而异如果你的发布页本质上是一个tab页希望用户反复进出那用switchTab没有问题。但我的设计里发布页是一个带表单的独立页面它有保存草稿、退出确认、图片上传等完整流程用navigateTo承载更合理。高德图标有一个常见问题如果你用了一个图标库的字体文件来做凸起按钮的图标在Android和iOS上的渲染会不一致有些字体图标在Android上会显示偏左或偏上因为字体基线不同。最稳妥的方案是用png图标并且根据不同屏幕密度提供多套尺寸。uniapp里通常的做法是放到static目录直接引用一个固定尺寸的png使用起来没问题但如果追求极致清晰度可以按2x、3x命名让系统自动选择。关于icon尺寸我建议凸起按钮的核心图标至少要做成88px x 88px的设计尺寸在750px设计稿下换到真正的物理像素上等于440px x 440px也就是iPhone 6的3x分辨率的尺寸。这样在大多数现代手机上都能保证清晰不会出现放大后边缘发虚的情况。3.4 自定义tabBar常见的能力补齐自定义tabBar虽然样式自由但能力需要自己补齐。主要有这么几项角标显示。官方tabBar的角标用uni.setTabBarBadge就能设置自定义后完全失效。你要自己维护一个badge状态存到store里tabBar组件渲染时读取然后用view画一个小红点或者数字。注意数字大于99的时候怎么显示通常是显示99。安全区适配。在iPhone X之后的全系机型上底部有Home Indicator区域。自定义tabBar要用env(safe-area-inset-bottom)去填充避免导航栏被系统黑条遮挡。我的处理方式是给整个自定义tabBar容器的padding-bottom设置为env(safe-area-inset-bottom)并且让凸起按钮的定位相对于这个容器内部计算确保有安全区的机型上布局不错位。切换动画。官方tabBar的页面切换没有过渡动画自定义tabBar反而可以做。在页面切换时用CSS的transition配合点按态给图标加一点缩放效果交互感受会好很多。但这只是点到为止的小动效做太多反而喧宾夺主。页面初始化时机。自定义tabBar是页面的一部分所以页面在reLaunch或者冷启动进入首页时tabBar会先和页面内容一起渲染。如果你的页面内容很重初始化的瞬间可能会看到底部导航先出来内容后加载有白屏感。一个成熟的优化是给tabBar组件添加v-if控制在页面onReady之后才渲染。不过这也意味着导航会比内容晚一点点出现权衡取舍要看具体场景。4. 两种方案的横向对比与选型建议两种方案之间的选择本质上是开发和维护成本、以及视觉自由度之间的权衡。我做一个表格来对比方便大家直观参考对比维度方案一官方tabBar cover-view悬浮方案二自定义tabBar实现难度较低改动集中在页面和组件样式较高需要自行管理切换逻辑与状态视觉效果上限受限cover-view的css支持有限几乎无限view的样式全部可用原生能力依赖依赖官方tabBar原生渲染稳定性高完全脱离官方tabBar全凭自己角标支持可用uni.setTabBarBadge需要自己开发存储状态后渲染安全区适配官方tabBar自带适配需要手动适配切换动画官方自带流畅需要自己追加且性能不可控兼容性风险中主要是cover-view在不同平台的行为差异中主要是页面栈和路由管理维护成本每个页面需要手动挂载组件单组件维护但路由逻辑需要统一适合场景只做中间凸起一个点其它保持原生视觉定制丰富交互需求多具体选型的时候我通常会抛给团队三个问题第一个你对底部导航的视觉改动是“点状”还是“面状”如果只是中间凸起官方tabBar加悬浮层就够如果要做背景渐变、沉浸式透明、侧滑抽屉等整体性的改变自定义tabBar是必然选择。第二个你们项目里对路由跳转的管理完善吗自定义tabBar对路由的依赖很强每个tab页面都必须用switchTab或reLaunch跳转。如果你的项目里有大量的api跳转、外部链接、扫码跳转页面等场景这些非用户点击的跳转要保证tabBar状态不错乱这很考验状态管理的设计。第三个团队的兼容性测试压力大吗方案一在Android的低端机上要重点测试cover-view的显示方案二则要重点测试路由切换的稳定性。选一个你觉得在目标用户机型分布中更可控的。从我给自己项目做选择的经验看绝大多数项目更适合方案一。因为需求一般都来自产品的一句“这里中间加一个大一点的按钮”产品不会要求你连tabBar的背景都重新画一遍。而方案二的自由度也带来自制力和维护成本的代价如果不是有明确的复杂需求不建议主动上。注意还有一个隐藏的考虑因素如果你同时要发布小程序端自定义tabBar方案会带来更多适配工作量。小程序端的自定义tabBar官方有专门的支持方式和App端细节不同两边都要兼顾的话后台维护的复杂度就不是乘二这么简单了而是每个端都有自己的坑。这种情况我建议优先保证App端的实现质量小程序端看运营策略再决定。5. 实测踩坑与排查实录这部分我把自己实际操作中遇到的问题集中写出来不少是在真机调试过程中花了不少时间才定位的。5.1 cover-view的transform偏移失效方案一中凸起按钮用position: fixed配合left: 50%和transform: translateX(-50%)来做水平居中在H5和微信小程序上都没问题但在某些Android App环境下transform的translateX会失效按钮会停在屏幕最左侧或者位置错乱。排查思路是先确认是不是cover-view本身对transform的支持问题。我后来发现Android的部分系统WebView版本对cover-view内部的transform支持不完整导致偏移无效。解决方式有两种。一种是放弃left: 50%加transform改用left: 0; right: 0; margin: 0 auto;让元素自己居中。但注意cover-view对margin: 0 auto的支持也有兼容性风险实测下来并不总是好使。另一种更推荐的思路是把组件做成一个撑满全屏的子窗体在页面层面直接定位.tabbar-center-btn-wrap { position: fixed; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 999; } .tabbar-center-btn { position: absolute; left: 50%; bottom: calc(20px env(safe-area-inset-bottom)); width: 104px; height: 104px; margin-left: -52px; border-radius: 50%; pointer-events: auto; }这个写法不依赖transform用margin-left负一半宽度来补偿居中兼容性明显更好。而且外层容器撑满全屏点击非按钮区域时因为pointer-events: none不会拦截页面交互只有按钮本身可以点击也不会影响原本页面的手势操作。5.2 中间的透明tab仍然可以点击跳转方案一里即使中间tab的图标和文字都做成透明的它对应的那部分tabBar区域仍然是官方tabBar的真身用户点上去不触发uni.switchTab也难。这个问题的体验很尴尬凸起按钮本身可以点但如果用户手滑点到凸起按钮下方、或者按钮上方一点点区域就会触发页面跳到发布页让人感觉莫名其妙。可以通过在每个tab页面里把switchTab的目的地拦截掉一部分请求来处理。一个更直接的思路是发布页做成一个简化版用户在非凸起区域误进入时提供一个快速的“返回”出口但这样体验还是不够好。目前没有官方办法让原生tabBar的某一格彻底透明且不可点击所以方案一始终带有这个体验瑕疵。在产品层面能做的对策是凸起按钮的尺寸尽量做大一些覆盖tabBar中间逻辑区域的大部分面积降低误触率。5.3 打包后凸起图标消失或变成了默认灰色图标这种问题常出现在方案一里我在新手阶段也遇到过。症状是H5端预览正常iOS真机调试正常但是打正式包之后凸起的图片没了偶尔变成灰色方块。根源是图标资源没有被正确打包。uniapp的static目录下的文件通常会被原样打包进去但如果你把图标放在一个自定义目录下或者用了动态拼接路径的方式引用图片在某些打包模式下这些资源会被tree-shaking掉或者路径被打乱了。无论方案一还是方案二强烈建议所有tabBar图标包括凸起按钮的图标都放在static根目录下并使用绝对路径引用比如/static/ic-publish.png。如果你确实要动态切换图标也要保证传入的路径是完整合法的绝对路径不要用相对路径拼接。5.4 自定义tabBar在苹果全面屏上布局被拉长自定义tabBar在iPhone X系列以上机型容易遇到布局异常原因是安全区适配没做对。很多人用fixed定位放置自定义tabBar的时候bottom只设了0没有考虑安全区结果导航栏有一部分陷进Home Indicator区域看起来就是内容被拉长或者说导航栏整体往下塌了一截。解决方式是给自定义tabBar的根元素设置padding-bottom: env(safe-area-inset-bottom)也可以在uniapp的全局样式里对这类机型做特殊处理。仅当目标是全面屏机型时env(safe-area-inset-bottom)才会返回一个非零的安全区高度其他机型返回0所以可以放心使用。5.5 切换页面时自定义tabBar高亮状态错乱自定义tabBar的高亮状态错乱是方案二遇到最多的一个坑。症状是用户从首页点进消息页又通过消息页里的一条push通知跳到我的页结果底部导航的高亮还停在消息页。排查方向非常明确高亮状态完全依赖每个页面onShow里同步activeIndex所以一定要确保所有可能进入tab页面的入口都做了同步。如果入口太多太分散除了在页面onShow里同步之外还要考虑从路由层面做一个统一的拦截器。uniapp没有直接暴露全局路由拦截的api但可以通过封装uni.switchTab和uni.navigateTo来实现类似效果// utils/route.js const originalSwitchTab uni.switchTab uni.switchTab function(options) { // 这里统一更新store里的activeIndex store.commit(setActiveTab, getTabIndexByUrl(options.url)) return originalSwitchTab(options) }这种做法虽然有点hack但在维护一个庞大的老项目时很管用能避免每个页面都去做同步的重复劳动。如果是新项目一开始就把activeIndex放到store里组件用computed去读然后页面统一在onShow中更新store就能从结构上避免这类问题。5.6 真机调试正常但发布版渲染异常最后一类问题发生在打包配置上。uniapp开发时用的是HBuilderX附带的调试基座发布版本用的是另一个打包环境两者的原生层版本有时不一致尤其是cover-view、subNVue这些依赖原生渲染能力的组件在打包时可能出现渲染行为不一致。遇到这种问题唯一的处理方式是在打包前就养成用自定义基座测试的习惯。HBuilderX允许你打一个自定义调试基座这个基座和你最终发布版的运行环境一致可以提前暴露很多兼容性问题。好不好用先不说这个步骤省不了。实操心得我做凸起按钮的时候先快速用方案一搭了一个demo结果发现cover-view在特定Android机型上的阴影渲染有问题临时改用自定义tabBar才通过。后来我也见过一个项目反过来一开始就上自定义tabBar却发现团队成员对路由状态管理不熟悉反而改了半天。所以两边都值得老老实实试一遍再做决定。6. 我的一些经验体会写了这么多最后按我的习惯聊几句实在的。中间凸起按钮这个需求看着是纯UI问题实际上牵扯到页面层级、路由设计、安全区适配、打包资源——哪一环不想到位都会出事。很多开发者在小程序平台做过类似需求就想着把同样的代码搬到App端结果被原生组件层级教育了一顿。uniapp这种跨端框架里忽略平台差异往往是最贵的教训。我自己兜兜转转过几次之后现在的选择标准非常简单如果需求只是在一两个tab页上放个凸起按钮我绝对选方案一简单直接改起来也快如果产品提出来的是“底部导航整体重新设计”那就早点切换到方案二然后把路由、状态、角标、安全区这些基础设施一次性做好后面收益会越来越大。无论如何建议你在项目里建一个独立的组件目录把这两个方案的核心能力都封装成可复用的组件和工具函数包括凸起按钮、tabBar容器、路由封装、状态同步。因为这类需求几乎每个App都逃不掉这次做好了下次换项目还能直接搬过去用。最后分享一个小技巧不管用哪种方案测试的时候一定要用真机跑一遍完整的页面流转路径包括从二级页面返回tab页、从通知栏点击进入某个tab页、冷启动直接打开某个tab页。只有走完全部路径底部导航的状态同步是否正常才能有结论。在模拟器和H5端跑出来的结果说服力都有限。
阅读完成 · 觉得有帮助?
咨询建站