1. 为什么需要自定义顶部导航栏
做微信小程序开发,如果你只是写个简单的工具或者内容展示页,用微信自带的导航栏配置一下颜色和标题,基本就够用了。但一旦涉及到稍微复杂一点的UI设计,比如设计师给了一个沉浸式背景图,要求导航栏背景透明,或者要在导航栏区域塞入搜索框、返回按钮加分享入口这种组合控件,原生的导航栏就显得力不从心了。这时候,自定义导航栏就成了必选项。
我最早遇到这个需求,是做一个电商类的小程序。产品经理希望首页的顶部是一个渐变色的背景,上面有城市定位、搜索框和消息入口。原生导航栏是纯色块,根本没法实现这种设计。另一个常见的场景是视频或阅读类小程序,为了最大化内容展示区域,需要把导航栏做得非常简洁,甚至隐藏标题,只保留一个返回的胶囊按钮。这些需求都指向同一个技术点:关闭原生导航栏,自己用View组件从头绘制一个。
听起来简单,不就是画个View放在顶部吗?但真动手做,你会发现一堆坑等着你:不同手机状态栏高度不一样,iOS和Android不一样;小程序右上角的胶囊按钮位置是固定的,你的自定义导航栏高度必须完美适配它,否则内容就会被遮挡或者出现难看的空白;在全面屏手机上,还需要考虑安全区域。这些细节如果处理不好,轻则UI错乱,重则功能按钮点不到。网上很多教程只给代码片段,缺了核心的原理分析和“踩坑”经验,照着做很容易出问题。所以,我把自己多次实战后总结的超详细流程和适配方案写下来,附上效果图和关键代码,希望能帮你一次搞定。
2. 核心原理与关键API剖析
自定义导航栏的本质,是关闭微信小程序默认的导航栏,并在页面最顶部,自己用View、Image、Text等基础组件搭建一个相同功能的区域。这里涉及到两个最核心的API:全局配置的navigationStyle和用于获取胶囊按钮位置的wx.getMenuButtonBoundingClientRect()。
2.1 启用自定义模式:navigationStyle
第一步,你需要在全局或页面的JSON配置文件中,将navigationStyle设置为custom。这告诉微信:“这个页面的导航栏我自己来画,你不用管了。”
全局设置 (app.json):
{ "window": { "navigationStyle": "custom" } }全局设置意味着所有页面都会使用自定义导航栏。如果你只有部分页面需要自定义,我更推荐在页面级进行设置,这样更灵活,也避免对不需要的页面产生意外影响。
页面设置 (页面对应的.json文件):
{ "navigationStyle": "custom" }设置为custom后,原生导航栏会消失,页面内容会直接顶到屏幕最顶部,位于手机状态栏(显示时间、电量的那一栏)的下方。这时候,你的页面顶部就是一片“空地”,需要你自己用代码把导航栏“填”上去。
2.2 获取布局的尺子:wx.getMenuButtonBoundingClientRect
这是整个自定义导航栏适配的灵魂。右上角的胶囊按钮(“...”菜单)是微信客户端绘制的,我们无法控制它的位置和大小。我们的自定义导航栏必须为这个胶囊按钮“让出”空间,或者以其为基准进行布局。
wx.getMenuButtonBoundingClientRect()这个API的作用,就是获取这个胶囊按钮在屏幕中的位置信息。它返回一个对象,包含以下关键属性(单位:px):
width: 胶囊按钮的宽度。height: 胶囊按钮的高度。top: 胶囊按钮上边界到屏幕顶部的距离。right: 胶囊按钮右边界到屏幕左边的距离。bottom: 胶囊按钮下边界到屏幕顶部的距离。left: 胶囊按钮左边界到屏幕左边的距离。
这里有一个非常重要的概念:top和bottom是相对于屏幕顶部的,而不是页面顶部。因为此时原生导航栏已消失,页面顶部就是屏幕顶部。top的值,基本上就是手机状态栏的高度加上一些系统预留的间距。
通过这个API,我们可以动态计算出我们自定义导航栏需要的高度。一个常见的公式是:自定义导航栏总高度 = 胶囊按钮bottom值 + 自定义的底部间距(例如8px)或者更保守一点:自定义导航栏总高度 = 胶囊按钮高度 + (胶囊按钮top值 - 系统状态栏高度) * 2实际上,我们通常直接使用胶囊按钮.bottom + 8px作为总高度,因为bottom已经包含了从屏幕顶部到胶囊按钮底部的全部距离,再加一点余量,确保导航栏内容完全在胶囊按钮下方,不会重叠。
2.3 系统状态栏高度的获取
胶囊按钮的top值包含了状态栏高度,但有时我们需要单独知道状态栏的高度,以便将标题或其他内容垂直居中。微信提供了wx.getSystemInfoSync()API,其中的statusBarHeight字段就是系统状态栏的高度(单位:px)。
所以,一个完整的自定义导航栏区域,通常由两部分组成:
- 状态栏占位区域:高度为
statusBarHeight,通常设置一个背景色,或者透明。 - 导航内容区域:从状态栏底部开始,到胶囊按钮底部结束(或加上余量)。这个区域里放置返回图标、标题、搜索框等自定义内容。
将这两部分的高度加起来,就是整个自定义导航栏的View容器应该设置的高度。
3. 从零开始的详细实现步骤
下面,我将以一个最常见的场景为例:实现一个带有返回按钮、居中标题和胶囊按钮(保留原功能)的自定义导航栏。我们会创建可复用的组件。
3.1 第一步:项目结构与配置
- 创建组件:在项目根目录下,新建一个
components文件夹(如果还没有),在里面创建custom-navigation-bar文件夹,并新建对应的组件文件:custom-navigation-bar.wxml,custom-navigation-bar.wxss,custom-navigation-bar.json,custom-navigation-bar.js。 - 声明组件:在
custom-navigation-bar.json中,将组件设置为自定义组件。{ "component": true, "usingComponents": {} } - 页面配置:在你需要使用自定义导航栏的页面的JSON文件中(例如
index.json),进行两项配置:{ "usingComponents": { "custom-navigation-bar": "/components/custom-navigation-bar/custom-navigation-bar" }, "navigationStyle": "custom" }
3.2 第二步:组件逻辑层(JS)实现
custom-navigation-bar.js是核心,负责计算所有关键尺寸。
// custom-navigation-bar.js Component({ properties: { // 组件外部传入的属性 title: { type: String, value: '默认标题' }, backgroundColor: { type: String, value: '#ffffff' }, color: { type: String, value: '#000000' }, showBack: { type: Boolean, value: true } }, data: { // 内部数据 statusBarHeight: 0, // 状态栏高度 navigationBarHeight: 0, // 整个自定义导航栏的高度 menuButtonHeight: 0, // 胶囊按钮高度 menuButtonTop: 0, // 胶囊按钮上边界距屏幕顶部距离 navigationContentHeight: 0, // 导航内容区域(不含状态栏)的高度 }, lifetimes: { attached() { // 在组件实例进入页面节点树时执行 this.calculateBarHeight(); } }, methods: { calculateBarHeight() { // 1. 获取系统信息 const systemInfo = wx.getSystemInfoSync(); // 2. 获取胶囊按钮信息 const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); // 3. 计算关键高度 // 状态栏高度 const statusBarHeight = systemInfo.statusBarHeight; // 导航内容区域高度:胶囊按钮高度 + (胶囊按钮上边距 - 状态栏高度) * 2 // 这个公式的目的是计算出从状态栏底部到胶囊按钮底部这个区域的高度。 // (menuButtonInfo.top - statusBarHeight) 是胶囊按钮上方的间隙。 // 通常上下间隙是对称的,所以乘以2。再加上胶囊按钮自身高度,就是内容区域总高。 const navigationContentHeight = (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height; // 4. 整个导航栏高度 = 状态栏高度 + 导航内容区域高度 const navigationBarHeight = statusBarHeight + navigationContentHeight; // 5. 更新组件数据 this.setData({ statusBarHeight: statusBarHeight, navigationBarHeight: navigationBarHeight, menuButtonHeight: menuButtonInfo.height, menuButtonTop: menuButtonInfo.top, navigationContentHeight: navigationContentHeight }); // 6. 可选:将计算出的导航栏高度传递给页面,方便页面内容容器设置上边距 this.triggerEvent('heightChange', { height: navigationBarHeight }); }, // 返回按钮点击事件 handleBack() { // 先尝试触发自定义事件,由页面决定如何处理(例如判断页面栈) this.triggerEvent('back'); // 如果页面没有处理,则默认返回上一页 if (!this._backEventHandled) { // 假设页面处理后会设置一个标志,这里仅为示意 wx.navigateBack(); } } } })关键计算逻辑解读:
menuButtonInfo.top - statusBarHeight:这部分计算的是胶囊按钮顶部到状态栏底部之间的间隙。这个间隙是系统预留的,用于视觉平衡。- 为什么乘以2?因为通常胶囊按钮上下方的间隙是相等的。所以
(menuButtonInfo.top - statusBarHeight)是上间隙,我们再假设下间隙与之相等,那么上下间隙的总和就是(menuButtonInfo.top - statusBarHeight) * 2。 - 最终的内容区域高度 = 上下间隙总和 + 胶囊按钮自身高度。这个高度能确保你的导航内容(如标题)在垂直方向上,与胶囊按钮大致处于同一水平线,视觉上最协调。
3.3 第三步:组件视图层(WXML)与样式(WXSS)
WXML 结构 (custom-navigation-bar.wxml):
<!-- 最外层容器,高度为计算出的总高度,背景色可自定义 --> <view class="custom-navigation-bar" style="height: {{navigationBarHeight}}px; background-color: {{backgroundColor}};"> <!-- 状态栏占位区域 --> <view class="status-bar" style="height: {{statusBarHeight}}px;"></view> <!-- 导航内容区域 --> <view class="navigation-content" style="height: {{navigationContentHeight}}px;"> <!-- 左侧返回按钮区域 --> <view class="left-area"> <block wx:if="{{showBack}}"> <view class="back-btn" bindtap="handleBack"> <!-- 这里可以用图片或Unicode字符,例如左箭头 --> <text class="back-icon">‹</text> <!-- 或者 <image src="/images/back.png" mode="widthFix"></image> --> </view> </block> </view> <!-- 中间标题区域 --> <view class="center-title" style="color: {{color}};"> {{title}} </view> <!-- 右侧区域,用于放置胶囊按钮或其他自定义内容 --> <!-- 注意:我们需要在这里为微信原生的胶囊按钮留出空间 --> <view class="right-area"> <!-- 这个占位块的宽度和位置必须与胶囊按钮对齐 --> <view class="menu-button-placeholder" style="width: {{menuButtonInfo ? menuButtonInfo.width : 0}}px;"> <!-- 占位,不放置任何可见内容 --> </view> </view> </view> </view>WXSS 样式 (custom-navigation-bar.wxss):
/* 清除默认边距 */ .custom-navigation-bar { width: 100%; position: fixed; /* 固定定位,悬浮在页面顶部 */ top: 0; left: 0; z-index: 9999; /* 确保导航栏在最上层 */ box-sizing: border-box; } .status-bar { width: 100%; /* 状态栏区域背景色通常与导航内容区域一致,或透明 */ } .navigation-content { width: 100%; display: flex; align-items: center; /* 垂直居中 */ justify-content: space-between; /* 左右两侧贴边,中间标题居中 */ padding: 0 16rpx; /* 左右内边距,可根据设计调整 */ box-sizing: border-box; } .left-area { flex-shrink: 0; /* 防止被压缩 */ display: flex; align-items: center; height: 100%; } .back-btn { padding: 16rpx; /* 增加点击热区 */ display: flex; align-items: center; justify-content: center; } .back-icon { font-size: 48rpx; line-height: 1; } .center-title { flex: 1; /* 占据剩余所有空间,实现居中 */ text-align: center; font-size: 36rpx; /* 小程序默认导航栏标题大小 */ font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 20rpx; /* 防止标题过长时紧贴两侧 */ } .right-area { flex-shrink: 0; display: flex; align-items: center; height: 100%; } .menu-button-placeholder { height: 100%; /* 这个占位块的高度由父级.navigation-content决定,宽度由JS传入的胶囊按钮宽度决定 */ /* 它本身是透明的,目的只是为了把中间标题的“flex:1”区域挤开,为胶囊按钮留出空间 */ }注意:
.menu-button-placeholder这个元素至关重要。因为微信的胶囊按钮是原生控件,始终悬浮在最上层。我们不能用我们的元素去覆盖它。所以策略是:在导航栏的右侧区域,放置一个与胶囊按钮等宽的透明占位块。这样,通过Flex布局的space-between或flex:1的特性,中间标题区域就会自动避开这个占位块,从而也避开了胶囊按钮的实际位置,实现了视觉上的对齐和功能上的不冲突。
3.4 第四步:在页面中使用组件
在页面的WXML文件中引入并使用组件:
<!-- index.wxml --> <!-- 引入自定义导航栏组件 --> <custom-navigation-bar id="customNavBar" title="我的首页" backgroundColor="#f8f8f8" color="#333" showBack="{{false}}" <!-- 首页通常不显示返回按钮 --> bind:heightChange="onNavigationBarHeightChange" bind:back="onBack" /> <!-- 页面内容区域 --> <!-- 关键:内容区域必须设置一个上边距(padding-top或margin-top),其值等于自定义导航栏的总高度,否则内容会被导航栏遮挡 --> <view class="page-content" style="padding-top: {{navBarHeight}}px;"> <!-- 你的页面具体内容在这里 --> <text>这里是页面的主体内容</text> </view>在页面的JS文件中接收导航栏高度并处理事件:
// index.js Page({ data: { navBarHeight: 0 // 用于存储导航栏高度,动态设置给内容容器 }, onLoad() { // 如果需要,可以在这里获取组件实例并读取高度 // 但更推荐通过事件监听 }, // 监听导航栏组件的高度变化事件 onNavigationBarHeightChange(e) { const height = e.detail.height; this.setData({ navBarHeight: height }); // 也可以将高度存入全局变量或本地存储,供其他页面组件使用 getApp().globalData.navBarHeight = height; }, // 处理返回按钮事件 onBack() { const pages = getCurrentPages(); if (pages.length > 1) { wx.navigateBack(); } else { // 如果是首页,可以跳转到指定页或提示 wx.switchTab({ url: '/pages/index/index' }); } } })4. 深入适配:全面屏、iOS与Android的差异处理
上面的基础实现能覆盖大部分情况,但在更复杂的设备和场景下,还需要进一步打磨。
4.1 安全区域(Safe Area)适配
全面屏手机(如iPhone X及以上机型、很多Android刘海屏、水滴屏手机)的屏幕角落是圆角,顶部有刘海,底部有黑条(Home Indicator)。为了确保内容不被这些区域遮挡,需要引入“安全区域”的概念。
微信小程序提供了wx.getSystemInfoSync()中的safeArea对象。它包含top,bottom,left,right,width,height属性,标识出了屏幕中绝对安全的内容区域。
对于自定义导航栏,我们主要关心safeArea.top。这个值表示安全区域上边界到屏幕顶部的距离,对于有刘海的手机,这个值就是状态栏高度加上刘海的物理高度。
优化方案: 在计算状态栏占位高度时,不再直接使用statusBarHeight,而是使用safeArea.top。因为safeArea.top已经包含了状态栏和刘海的高度,这样能确保你的导航栏内容起始于安全区域之内。
修改组件JS中的计算:
calculateBarHeight() { const systemInfo = wx.getSystemInfoSync(); const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); // 使用 safeArea.top 作为状态栏区域的高度基准 const statusBarHeight = systemInfo.safeArea.top; // 关键修改点 // 注意:胶囊按钮的top是相对于屏幕顶部的,包含了safeArea.top以上的部分。 // 所以后续计算导航内容区域高度的逻辑 (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height // 依然成立,因为它计算的是从安全区域顶部(safeArea.top)到胶囊按钮底部的距离。 // 但是,这里有一个潜在的坑:在非全面屏手机上,safeArea.top 可能等于 statusBarHeight。 // 而在一些Android异形屏上,safeArea.top 可能为0。我们需要一个兼容性处理。 const safeTop = systemInfo.safeArea ? systemInfo.safeArea.top : systemInfo.statusBarHeight; const statusBarHeight = safeTop > 0 ? safeTop : systemInfo.statusBarHeight; const navigationContentHeight = (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height; const navigationBarHeight = statusBarHeight + navigationContentHeight; this.setData({ statusBarHeight: statusBarHeight, navigationBarHeight: navigationBarHeight, // ... 其他数据 }); }同时,页面内容容器的上边距也应该使用这个更新后的navigationBarHeight。
4.2 iOS与Android的胶囊按钮差异
实测中发现,在部分Android机型(特别是某些厂商定制系统)上,wx.getMenuButtonBoundingClientRect()返回的胶囊按钮top值可能为0或异常小。这会导致我们的计算公式失效。
处理方案:设置一个兜底的最小高度。
calculateBarHeight() { // ... 获取 systemInfo 和 menuButtonInfo let statusBarHeight = systemInfo.statusBarHeight; const safeTop = systemInfo.safeArea ? systemInfo.safeArea.top : statusBarHeight; statusBarHeight = safeTop > 0 ? safeTop : statusBarHeight; // 处理胶囊按钮信息异常 let menuTop = menuButtonInfo.top; let menuHeight = menuButtonInfo.height; // 如果获取到的top值异常(例如为0或小于状态栏高度),使用一个经验值 // 经验值:iOS通常约48px, Android约56px(这里指从屏幕顶部到胶囊按钮中心的估算距离) // 我们可以根据状态栏高度来估算一个合理的menuTop if (!menuTop || menuTop < statusBarHeight) { // 根据不同平台设置一个默认的胶囊按钮上边距 const isIOS = systemInfo.platform === 'ios'; menuTop = statusBarHeight + (isIOS ? 6 : 8); // 经验值,可根据UI效果微调 menuHeight = isIOS ? 32 : 40; // 经验值 } const navigationContentHeight = (menuTop - statusBarHeight) * 2 + menuHeight; const navigationBarHeight = statusBarHeight + navigationContentHeight; this.setData({ statusBarHeight: statusBarHeight, navigationBarHeight: navigationBarHeight, menuButtonHeight: menuHeight, menuButtonTop: menuTop, navigationContentHeight: navigationContentHeight }); }4.3 页面滚动与吸顶效果
自定义导航栏使用了position: fixed固定在顶部。当页面滚动时,它始终会停留在顶部,这通常是我们想要的效果(即吸顶)。但需要注意一个细节:fixed定位的元素是脱离正常文档流的。
这意味着,页面内容容器设置的padding-top(等于导航栏高度)是为了在初始位置给导航栏留出空间。当页面滚动时,导航栏悬浮在上方,这个padding-top区域在视觉上就变成了导航栏后面的背景。如果你的导航栏背景是半透明或渐变的,滚动时页面内容在这个区域“滑动”的效果需要被正确处理。
通常这不是问题。但如果你发现滚动时,导航栏背景和内容衔接处有闪烁或不自然,可以尝试给导航栏容器加上backdrop-filter: blur(10px)(iOS毛玻璃效果,安卓支持不佳)或确保其背景色为纯色。
5. 进阶效果与实战踩坑记录
掌握了基础实现和适配后,我们可以玩出更多花样,同时也分享几个我实际踩过的坑。
5.1 实现沉浸式渐变背景导航栏
设计师常要求导航栏背景是一张渐变色或者一张延伸到导航栏的背景图的一部分。由于导航栏是fixed定位,我们需要让页面内容区域向上延伸,覆盖掉导航栏的padding-top区域,并通过z-index控制层级。
思路:
- 页面内容区域不再设置
padding-top,而是设置一个margin-top,值为导航栏高度。这样内容是从导航栏下方开始布局的。 - 在页面内容的最顶部,创建一个与导航栏等高的背景元素(例如一个
View),给它设置渐变色或背景图,并赋予position: sticky或position: relative定位。 - 通过
z-index控制,让自定义导航栏(z-index: 9999)悬浮在这个背景元素之上,但位于其内容(如文字、按钮)之下。
页面WXML调整示例:
<!-- index.wxml --> <custom-navigation-bar title="沉浸式首页" backgroundColor="transparent" <!-- 导航栏背景设为透明 --> color="#fff" <!-- 标题和图标设为白色 --> showBack="{{true}}" /> <view class="page-content" style="margin-top: {{navBarHeight}}px;"> <!-- 顶部背景元素 --> <view class="top-background" style="height: {{navBarHeight}}px; background: linear-gradient(to right, #ff5e7d, #ff9900);"> <!-- 这个元素只提供背景 --> </view> <!-- 实际页面内容 --> <view class="content-wrapper" style="position: relative; z-index: 1;"> <!-- 你的页面具体内容在这里 --> </view> </view>页面WXSS:
.custom-navigation-bar { /* 确保导航栏内容在顶层 */ z-index: 10000; } .top-background { position: absolute; top: -{{navBarHeight}}px; /* 向上偏移,覆盖margin-top留出的空间 */ left: 0; width: 100%; z-index: 0; /* 层级低于导航栏,高于内容 */ } .content-wrapper { position: relative; background-color: #fff; /* 内容区域有自己的背景 */ border-radius: 24rpx 24rpx 0 0; /* 可选,顶部圆角 */ margin-top: -20rpx; /* 可选,让内容稍微上移与背景融合 */ }这样,滚动时,.top-background会随着页面内容移动,而.custom-navigation-bar则固定在上方,透过其透明背景看到下面的渐变,实现了沉浸式效果。
5.2 胶囊按钮区域的自定义交互
微信原生的胶囊按钮点击会弹出菜单。但有时我们希望在胶囊按钮所在的区域放置我们自己的图标(比如分享、更多)。由于无法隐藏或覆盖原生胶囊按钮,我们可以采用“扩展点击区域”的思路。
在之前的结构中,我们已经在右侧区域放置了一个与胶囊按钮等宽的.menu-button-placeholder作为占位。我们可以在这个占位块的旁边(左侧)放置我们的自定义图标。
<!-- 在 .right-area 内修改 --> <view class="right-area"> <!-- 自定义分享按钮,放在胶囊按钮占位块的左边 --> <view class="custom-share-btn" bindtap="onShare" wx:if="{{showShare}}"> <image src="/images/share.png" mode="widthFix" style="width: 40rpx; height: 40rpx;"></image> </view> <!-- 胶囊按钮占位块 --> <view class="menu-button-placeholder" style="width: {{menuButtonWidth}}px;"></view> </view>.right-area { display: flex; align-items: center; } .custom-share-btn { padding: 16rpx; margin-right: 16rpx; /* 与胶囊按钮占位块保持间距 */ }这样,用户点击分享图标时触发我们的事件,点击右侧的占位块区域(即原生胶囊按钮上方)则会弹出微信菜单。两者互不干扰。
5.3 实际开发中遇到的坑与解决方案
坑1:自定义导航栏在部分Android机型上闪烁或抖动。
- 现象:页面滚动时,导航栏有时会轻微上下移动或闪烁。
- 原因:这可能与页面滚动事件、CSS的
transform或fixed定位在旧版本WebView中的渲染bug有关。也可能是计算高度时,数值不是整数导致亚像素渲染问题。 - 解决:
- 确保计算出的
navigationBarHeight等值是整数(用Math.floor()取整)。 - 给导航栏容器添加CSS属性
transform: translateZ(0);或will-change: transform;来开启GPU加速,促进图层合成,减少渲染问题。 - 检查页面是否有频繁的
setData操作影响了布局。
- 确保计算出的
坑2:页面切换(如wx.navigateTo)时,自定义导航栏会短暂消失再出现。
- 现象:从A页面跳转到B页面(B页面有自定义导航栏),B页面的导航栏会先显示原生样式(或空白),然后才渲染出自定义组件。
- 原因:页面跳转动画期间,新页面开始渲染。自定义组件需要时间进行初始化、计算尺寸和渲染。而原生导航栏的隐藏(
navigationStyle: custom)与应用组件渲染之间存在时间差。 - 解决:这是一个视觉体验问题,很难彻底消除。可以尝试以下方法缓解:
- 给导航栏容器设置一个与最终背景色相同的初始背景色,避免出现白色闪屏。
- 将导航栏高度的计算逻辑提前,例如在
app.js的onLaunch中计算并存入全局变量,页面组件直接从全局变量读取,减少计算和渲染时间。 - 使用
wx.nextTick确保在页面初次渲染完成后再进行可能引起布局抖动的操作。
坑3:在tabBar页面使用自定义导航栏,切换Tab时布局错乱。
- 现象:Tab页A有自定义导航栏,切换到Tab页B(没有自定义导航栏)再切回来,A页面的导航栏位置或样式不对。
- 原因:Tab页切换不是完整的页面生命周期,
attached等组件生命周期函数可能不会每次都触发。高度计算可能未执行。 - 解决:在组件中,使用
pageLifetimes来监听页面显示事件。// custom-navigation-bar.js Component({ // ... properties, data 等 pageLifetimes: { show() { // 当组件所在的页面被展示时执行,包括Tab切换回来 this.calculateBarHeight(); }, }, // ... lifetimes, methods });
坑4:导航栏标题过长被截断,或在小屏手机上布局拥挤。
- 解决:在中间标题的样式中,我们已经设置了
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;来处理长标题。对于小屏手机,可能需要动态调整字体大小或左右区域的宽度。可以通过wx.getSystemInfoSync().screenWidth获取屏幕宽度,在组件中根据屏幕宽度动态计算一个最大标题宽度,并通过样式绑定到标题元素上。