UniApp 自定义标题栏:从原理到实战
在 UniApp 开发中,原生导航栏虽然能满足基础需求,但在个性化设计、多端适配细节控制等场景下存在局限性。本文将详细讲解自定义标题栏的实现原因、完整步骤及核心意义,帮助开发者打造适配多端的个性化导航栏。
一、为什么要自定义标题栏?
UniApp 提供的原生导航栏(navigationBar)虽然开箱即用,但在实际开发中存在以下痛点:
-
样式定制受限:原生导航栏仅支持修改背景色、文字内容等基础样式,无法实现渐变背景、自定义图标布局、特殊文字排版等个性化设计; -
多端适配差异:不同平台(微信小程序、App、H5)的原生导航栏样式、高度、胶囊按钮位置存在差异,原生方案难以统一体验; -
交互逻辑扩展难:原生导航栏的返回、菜单等交互逻辑固定,无法灵活添加自定义按钮(如分享、搜索)或修改返回逻辑; -
布局控制不足:原生导航栏与页面内容的间距、层级等无法精细控制,容易出现内容遮挡或布局错位问题。
自定义标题栏可彻底解决以上问题,实现 100% 自定义样式与交互,保证多端视觉和体验一致性。
二、UniApp 自定义标题栏实现步骤
步骤 1:关闭原生导航栏
首先需要在 pages.json 中配置关闭页面的原生导航栏,开启自定义模式:
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
// 关键配置:关闭原生导航栏
"navigationStyle": "custom"
}
}
]
-
navigationStyle: "custom":表示当前页面使用自定义导航栏,原生导航栏会被隐藏; -
该配置需在需要自定义标题栏的页面单独设置(也可在 globalStyle中全局配置)。
步骤 2:封装自定义标题栏组件
创建通用的标题栏组件(components/mod-nav-bar/mod-nav-bar.vue),核心包含固定显示层和占位层,避免页面内容被遮挡。
2.1 组件脚本(script setup)
<script setup>
import { computed } from "vue";
import { COLOR_THEME_PRIMARY } from "@/utils/config.js";
import { useNavBarStyle } from "@/utils/system.js";
// 获取系统计算的导航栏高度(适配不同设备)
const { statusBarHeight, titleBarHeight, headHeight } = useNavBarStyle();
// 定义组件属性:标题、标题颜色
const props = defineProps({
title: {
type: String,
default: ""
},
titleColor: {
type: String,
default: '#000'
}
});
// 判断是否显示返回按钮(页面栈长度>1时显示)
const showBack = getCurrentPages().length > 1;
// 动态计算标题文字对齐方式(无返回按钮时左对齐,有则居中)
const titleTextAlign = computed(() => {
if (!showBack) return 'left';
return 'center';
});
// 自定义返回逻辑(失败时返回首页)
const navBack = () => {
uni.navigateBack({
fail: () => {
uni.reLaunch({
url: "/pages/index/index"
});
}
});
};
</script>
2.2 组件模板(template)
<template>
<view class="mod-nav-bar">
<!-- 固定显示的导航栏(吸顶) -->
<view class="fixed-wrap">
<!-- 状态栏占位(适配手机顶部状态栏高度) -->
<view class="status-bar"></view>
<!-- 标题栏核心区域 -->
<view class="title-bar">
<!-- 返回按钮 -->
<view class="arrow-wrap" v-if="showBack" @click.stop="navBack">
<uni-icons class="icon" type="left" size="28" :color="titleColor"></uni-icons>
</view>
<!-- 标题文字 -->
<view class="text-wrap">
{{ title }}
</view>
<!-- 右侧菜单占位(可扩展自定义按钮) -->
<view class="menu-wrap"></view>
</view>
</view>
<!-- 占位层:防止页面内容被固定导航栏遮挡 -->
<view class="block-wrap"></view>
</view>
</template>
2.3 组件样式(style scoped)
<style lang="scss" scoped>
.mod-nav-bar {
width: 750rpx; // 适配uniapp的rpx单位
.fixed-wrap {
position: fixed;
left: 0;
top: 0;
width: 100%;
background: v-bind(COLOR_THEME_PRIMARY); // 动态绑定主题色
z-index: 8000; // 保证导航栏在最上层
.status-bar {
width: 100%;
height: v-bind(statusBarHeight); // 状态栏高度(动态计算)
}
.title-bar {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
height: v-bind(titleBarHeight); // 标题栏高度(动态计算)
color: v-bind(titleColor);
padding: 0 32rpx;
font-size: 32rpx;
.arrow-wrap {
height: 100%;
width: 80rpx;
flex-shrink: 0;
display: flex;
align-items: center; // 返回箭头垂直居中
}
.text-wrap {
flex: 1;
text-align: v-bind(titleTextAlign); // 动态对齐标题
font-weight: bolder;
}
.menu-wrap {
height: 100%;
width: 80rpx;
flex-shrink: 0; // 固定右侧宽度
}
}
}
// 占位层:高度与导航栏一致,避免内容上移
.block-wrap {
width: 100%;
height: v-bind(headHeight);
}
}
步骤 3:封装系统高度计算工具(system.js)
不同设备的状态栏、胶囊按钮高度不同,需动态计算以保证适配:
import { computed, unref } from "vue";
import { SYSTEM_WINDOW_INFO, MENU_BUTTON_RECT_INFO } from "@/utils/config.js";
// 状态栏高度(px),默认25px兜底
export const statusBarH = computed(() => SYSTEM_WINDOW_INFO.statusBarHeight || 25);
// 标题栏高度:基于胶囊按钮位置计算(适配微信小程序)
export const titleBarH = computed(() => {
const { top, height } = MENU_BUTTON_RECT_INFO;
if (!top || !height) return 40; // 兜底高度
// 胶囊按钮到状态栏的距离 *2 + 胶囊高度 = 标题栏高度
return height + ((top - unref(statusBarH)) * 2);
});
// 导航栏总高度(状态栏+标题栏)
export const navBarH = computed(() => unref(statusBarH) + unref(titleBarH));
// 对外暴露样式用的高度(带px单位)
export const useNavBarStyle = () => {
const statusBarHeight = computed(() => unref(statusBarH) + "px");
const titleBarHeight = computed(() => unref(titleBarH) + 'px');
const headHeight = computed(() => unref(navBarH) + 'px');
return {
statusBarHeight,
titleBarHeight,
headHeight
};
};
步骤 4:配置系统常量(config.js)
提前获取系统信息和胶囊按钮信息,避免重复计算:
// 获取系统窗口信息(包含状态栏高度)
export const SYSTEM_WINDOW_INFO = uni.getWindowInfo();
// 获取胶囊按钮位置信息(微信小程序特有,适配导航栏高度)
export const MENU_BUTTON_RECT_INFO = uni.getMenuButtonBoundingClientRect ? uni.getMenuButtonBoundingClientRect() : {};
// 主题色常量(可根据项目自定义)
export const COLOR_THEME_PRIMARY = "#ffffff";
步骤 5:页面中使用自定义标题栏
在需要的页面(如 pages/index/index.vue)中引入并使用组件:
<template>
<view class="page-wrap">
<!-- 自定义标题栏:传入标题和文字颜色 -->
<mod-nav-bar title="首页" title-color="#fff"></mod-nav-bar>
<!-- 页面其他内容 -->
<view class="page-content">
首页内容...
</view>
</view>
</template>
<script setup>
// 引入自定义标题栏组件
import modNavBar from "@/components/mod-nav-bar/mod-nav-bar.vue";
</script>
已关注
关注
重播 分享 赞
三、自定义标题栏的核心意义
-
体验一致性:统一多端(小程序、App、H5)导航栏样式,避免原生导航栏的平台差异,提升用户体验; -
样式高度自定义:支持渐变背景、自定义图标、特殊排版等原生导航栏无法实现的设计效果,满足UI设计需求; -
交互灵活扩展:可自由添加返回、分享、搜索、更多等自定义按钮,修改返回逻辑(如失败时返回首页),适配复杂业务场景; -
布局精准控制:通过“固定层+占位层”的设计,避免页面内容被遮挡,保证布局稳定性; -
适配性更强:基于系统信息动态计算高度,适配不同机型(如刘海屏、全面屏)和操作系统,避免出现导航栏高度异常问题。
总结
-
自定义标题栏的核心是先通过 navigationStyle: custom关闭原生导航栏,再通过组件实现自定义布局; -
关键适配点是基于系统信息(状态栏、胶囊按钮)动态计算导航栏高度,保证多端/多机型兼容; -
“固定层+占位层”的设计模式是避免页面内容遮挡的核心,也是自定义导航栏的最佳实践。
通过以上步骤,即可实现一套适配多端、样式灵活、交互可控的 UniApp 自定义标题栏,满足各类个性化开发需求。
夜雨聆风
