自定义导航栏
微信小程序使用 navigationStyle: "custom" 时,需自行处理状态栏高度、胶囊避让和页面布局。
核心公式
微信小程序导航栏高度(uni-ui 官方):
导航栏高度 = 胶囊高度 + (胶囊top - 状态栏高度) × 2
总高度 = 状态栏高度 + 导航栏高度1
2
2
H5 / App 无胶囊,navBarHeight = 44。
平台差异
| 平台 | 状态栏高度 | 胶囊按钮 | 处理方式 |
|---|---|---|---|
| 微信小程序 | 动态获取(20-44px) | 有 | 需要避让胶囊 |
| H5 | 0 | 无 | 不需要避让 |
| App | 动态获取 | 无 | 不需要避让 |
使用 useNavbar
项目封装了 useNavbar composable 统一处理导航栏计算:
ts
import { useNavbar } from "@/composables/useNavbar"
// 基本用法
const { statusBarHeight, totalHeight, contentPaddingTop } = useNavbar()
// TabBar 页面
const navbar = useNavbar({ hasTabbar: true })1
2
3
4
5
6
7
2
3
4
5
6
7
返回值
| 属性 | 类型 | 说明 |
|---|---|---|
statusBarHeight | Ref<number> | 状态栏高度 |
navBarHeight | number | 导航栏内容高度 |
totalHeight | ComputedRef<number> | 导航栏总高度 |
contentPaddingTop | ComputedRef<string> | 页面 paddingTop |
safeAreaBottom | Ref<number> | 安全区域底部高度 |
menuButton | Ref<MenuButtonRect | null> | 胶囊按钮信息 |
menuButtonRightGap | ComputedRef<number> | 胶囊按钮右侧距离 |
menuButtonLeft | ComputedRef<number> | 胶囊按钮左侧距离 |
contentWidth | ComputedRef<number> | 内容区域可用宽度 |
使用 custom-navbar 组件
页面直接使用 <custom-navbar> 即可,组件内部调用 useNavbar() 处理所有高度计算。
vue
<custom-navbar title="页面标题" fixed placeholder />1
Props:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| title | 标题文字 | string | "" |
| titleColor | 标题颜色 | string | var(--color-text) |
| bgColor | 背景色 | string | var(--color-bg) |
| iconColor | 图标颜色 | string | var(--color-text) |
| showBack | 是否显示返回按钮 | boolean | true |
| showHome | 是否显示首页按钮 | boolean | false |
| backIcon | 返回按钮图标名 | string | "arrow-left" |
| backIconSize | 返回按钮图标大小 | string | "40rpx" |
| fixed | 是否固定到顶部 | boolean | true |
| placeholder | fixed 时是否自动占位 | boolean | false |
| navBarHeight | 导航栏内容高度 | number | 44 |
Events:
| 事件 | 说明 |
|---|---|
back | 点击返回按钮时触发 |
home | 点击首页按钮时触发 |
Slots:
| 插槽 | 说明 |
|---|---|
left | 左侧区域(返回/首页按钮之后) |
center | 中间区域(标题之后,标题用 #center 可替换) |
right | 右侧区域 |
场景模板
TabBar 页面 + 非沉浸式
内容从导航栏下方开始,placeholder 自动占位。
vue
<template>
<view class="page page--tabbar">
<custom-navbar title="首页" :show-back="false" fixed placeholder />
<view class="content">
<!-- 页面内容 -->
</view>
</view>
</template>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
TabBar 页面 + 沉浸式
轮播图铺到顶部,导航栏透明叠加。
vue
<template>
<view class="page page--tabbar">
<view class="hero" :style="{ paddingTop: `${navbar.totalHeight.value}px` }">
<custom-navbar
fixed :placeholder="false"
bg-color="transparent"
title-color="var(--color-text-inverse)"
icon-color="var(--color-text-inverse)"
:show-back="false"
/>
<wd-swiper :list="swiperList" autoplay />
</view>
<view class="content">
<!-- 其余内容 -->
</view>
</view>
</template>
<script setup>
import { useNavbar } from "@/composables/useNavbar"
const navbar = useNavbar({ hasTabbar: true })
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
普通页面 + 自定义导航栏
vue
<template>
<view class="page">
<custom-navbar title="详情" fixed placeholder />
<view class="content">
<!-- 页面内容 -->
</view>
</view>
</template>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
scroll-view 固定列表
列表区域独立滚动,导航栏固定。
vue
<template>
<view class="page">
<custom-navbar title="列表" fixed placeholder />
<scroll-view scroll-y :style="scrollStyle">
<!-- 列表内容 -->
</scroll-view>
</view>
</template>
<script setup>
import { useNavbar } from "@/composables/useNavbar"
const navbar = useNavbar({ hasTabbar: false })
const scrollStyle = computed(() => ({
height: `calc(100vh - ${navbar.totalHeight.value}px)`,
}))
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
常见坑点
| 坑点 | 原因 | 解决 |
|---|---|---|
| 内容与导航栏重叠 | 未设 paddingTop 或 placeholder | 使用 placeholder 或 paddingTop: totalHeight |
| 页面未超高却能滚动 | min-height: 100vh 未减去导航栏 | 优先不设 min-height,让内容自然撑开 |
| 按钮与胶囊重叠 | 未避让胶囊按钮 | 使用 menuButtonRightGap |
getMenuButtonBoundingClientRect 返回 0 | iOS 预览时偶发 | useNavbar 内置重试与降级 |
原则:
- 不要依赖
--status-bar-height(小程序端固定 25px) - 不要无脑
min-height: 100vh - 不要重复叠加占位(系统导航栏 + 自定义 placeholder 同时存在)
- TabBar 页面滚动容器
bottom需减去 TabBar + safeAreaBottom
