<p>微信小程序中导航栏高度需用 statusBarHeight 加胶囊按钮计算,公式为 height + (top - statusBarHeight) × 2;App 端建议禁用原生导航栏并自定义,H5 和 iOS/Android 需条件编译处理。</p>

uni.getSystemInfoSync() 返回的 windowHeight 不是导航栏高度
很多人误以为 windowHeight 是「可用区域高度」就等于「去除了导航栏后的高度」,于是用 screenHeight - windowHeight 硬算导航栏——这在 H5 和部分 Android App 上可能碰巧对,但在微信小程序里完全不可靠。因为 windowHeight 在小程序端已剔除状态栏 + 原生导航栏(如果开启),但你无法反推原生导航栏是否开启、是否被隐藏、是否叠加了自定义内容。真正需要的「系统导航栏高度」,其实是「状态栏 + 标题栏(含胶囊)」这一整块顶部 UI 的总高。
微信小程序必须用 uni.getMenuButtonBoundingClientRect() 算标题栏
微信小程序没有直接提供「导航栏高度」API,但提供了两个关键信息源:statusBarHeight(来自 uni.getSystemInfoSync())和胶囊按钮位置(uni.getMenuButtonBoundingClientRect())。后者返回对象包含 top(胶囊上边距屏幕顶)、height(胶囊自身高度),而标题栏高度 = height + (top - statusBarHeight) * 2。
- 这个公式成立的前提是:胶囊在标题栏中垂直居中,所以顶部空隙 = 底部空隙 =
top - statusBarHeight -
uni.getMenuButtonBoundingClientRect()在小程序中是同步函数,但**必须确保页面已挂载且渲染完成**,否则返回null或默认值(如{ top: 0, height: 0 }) - 不要在
data初始化时调用它;应在onShow或onMounted(Vue3)中执行,必要时加nextTick等待 DOM 就绪
Android 和 iOS App 端不能只依赖 statusBarHeight
App 端(iOS/Android)的原生导航栏高度不固定:statusBarHeight 只给状态栏,而原生导航栏(如 iOS 的 44px、Android 的 56dp)并不暴露在 uni API 中。如果你启用了 "navigationStyle": "custom",那整个顶部区域就是你全权控制的;但如果用的是默认导航栏,uni-app 不会告诉你它的像素高度。
- 实际开发中,App 端更推荐统一禁用原生导航栏(
"navigationStyle": "custom"),然后自己用statusBarHeight+ 手动设定标题栏高度(如 88rpx)来布局 - 若必须适配原生导航栏,iOS 可按 44px +
statusBarHeight估算,Android 则差异极大(有刘海、水滴屏、MIUI/EMUI 自定义栏),无通用值 -
safeAreaInsets在 App 端虽可读,但仅反映安全区偏移(如底部指示条),和导航栏高度无关
别漏掉单位换算和平台条件编译
所有通过 API 拿到的高度单位都是 px,但 uni-app 模板中常用 rpx。直接把 statusBarHeight 当 rpx 用会导致 iPhone 上偏小、安卓上偏大——因为 rpx 基准是屏幕宽度,不是高度。
- 正确换算方式:
const rpx = Math.round(px / systemInfo.screenWidth * 750) - 不同平台的胶囊按钮存在性不同:iOS App 和 H5 没有胶囊,
uni.getMenuButtonBoundingClientRect()返回null或抛错,必须加判断 - 务必用条件编译包裹逻辑:
#ifdef MP-WEIXIN下才执行胶囊计算,#ifdef APP-PLUS下走固定值或 statusBarHeight + 88rpx - 测试真机!模拟器常返回泛化值(如
statusBarHeight: 20),iPhone 15 Pro 实际可能是 59px
真实项目里最易被忽略的点:你以为拿到了「导航栏高度」,其实只是「顶部可交互区域」的视觉高度;而滚动时状态栏渐变、下拉刷新触发器位置、键盘弹起后安全区变化……这些动态场景会让静态计算失效。所以,高度值尽量只用于初始布局,涉及滚动/动画的交互,得结合 onPageScroll 或 resize 事件重算。


















