《UniApp 安全区适配与自定义导航栏全攻略》
在 UniApp 开发原生 App(比如用 HBuilderX 打包成 Android / iOS)时,要预留手机的系统状态栏(即顶部信号、电量栏)空间,否则内容会被顶到最上方被遮住。
⚠️ 注意事项
- App 端的状态栏一般为 24–44px(取决于设备和刘海屏)。 
- iPhone X 以上需同时考虑 顶部状态栏 + 底部安全区。 
- 如果使用 - <page-meta>设置导航栏为透明,也要保留- status-bar。
- HBuilderX运行时调试时,只有 真机预览或打包后 才能看到真实安全区效果。
| 场景 | 变量 | 含义 | 
|---|---|---|
| iPhone X 刘海屏 | headerSafeHeight | 顶部留出状态栏空间 | 
| 带 Home 指示条的设备 | bottomSafeHeight | 底部留出安全区 | 
| 横屏设备或Pad | leftSafeWidth/rightSafeWidth | 侧边安全区 | 
提示
在 App 真机运行 时才会正确返回
safeAreaInsets,
模拟器/H5 里返回 0 是正常的。
如果页面使用了自定义导航栏,必须手动加上
<view :style="{height: headerSafeHeight+'px'}"></view>。
| 模块 | 说明 | 是否包含 | 
|---|---|---|
| ✅ 状态栏预留(var(--status-bar-height)) | 自动适配刘海屏 | ✔️ | 
| ✅ 动态获取 safeAreaInsets | 手动控制 / JS方式 | ✔️ | 
| ✅ 封装 safeAreaMixin | 支持 top/bottom/left/right | ✔️ | 
| ✅ 封装自定义导航栏 | 可复用组件 | ✔️ | 
| ✅ App、小程序、H5 平台兼容 | 各平台都考虑到了 | ✔️ | 
| ✅ 返回逻辑处理 | 页面栈判断(navigateBack/switchTab) | ✔️ | 
| ✅ 插槽 + 动态样式 | 支持左右插槽、右侧按钮 | ✔️ | 
| ✅ 关闭系统导航栏配置 | navigationStyle: "custom" | ✔️ | 
| ✅ 状态栏占位 + 内容区域 | 顶部 + 底部安全区布局 | ✔️ | 
一、官方推荐方式:使用 status-bar 占位区(最简单)
UniApp 内置了系统状态栏的安全区变量,可以自动适配不同手机。
📱
var(--status-bar-height)是 UniApp 提供的全局 CSS 变量,在 App 端和小程序端都能自动匹配手机状态栏高度(包括刘海屏)。
<template><view class="page"><!-- ✅ 状态栏占位 --><view class="status-bar"></view><!-- 页面内容 --><view class="content"><text>这是主内容区域</text></view></view>
</template><style>
.status-bar {height: var(--status-bar-height); /* 自动适配刘海/状态栏高度 */width: 100%;background-color: #ffffff; /* 可自定义颜色 */
}
.content {flex: 1;background-color: #f5f5f5;
}
</style>
二、使用 JavaScript 获取安全区信息(更灵活)
如果你需要在脚本中动态设置(比如传给子组件):
onLoad() {const info = uni.getSystemInfoSync();console.log('状态栏高度:', info.statusBarHeight);this.statusBarHeight = info.statusBarHeight;
}
然后在模板中用:
<view :style="{ height: statusBarHeight + 'px' }"></view>
三、推荐封装方式(可复用)
建一个 safeAreaMixin.js(很多项目这样做):
// /mixins/safeAreaMixin.js/*** 安全区域适配混入* 用于自动适配顶部状态栏和底部安全区*/
export default {data() {return {safeAreaInsetsTop: 0,     // 顶部安全区高度safeAreaInsetsBottom: 0,  // 底部安全区高度safeAreaInsetsLeft: 0,safeAreaInsetsRight: 0,statusBarHeight: 0,       // 状态栏高度isH5: false,isApp: false}},computed: {/** ✅ 顶部安全区高度(App、微信小程序会生效) */headerSafeHeight() {// H5 不需要状态栏占位// #ifdef H5return 0// #endif// 其他平台返回状态栏高度或安全区顶部高度return this.safeAreaInsetsTop || this.statusBarHeight || 0},/** ✅ 底部安全区高度(适配 iPhone X 系列) */bottomSafeHeight() {// #ifdef H5return 0// #endifreturn this.safeAreaInsetsBottom || 0},/** ✅ 左右安全边距 */leftSafeWidth() {return this.safeAreaInsetsLeft || 0},rightSafeWidth() {return this.safeAreaInsetsRight || 0}},onLoad() {this.initSafeArea()},methods: {/** 初始化安全区域信息 */initSafeArea() {// 平台标识// #ifdef H5this.isH5 = true// #endif// #ifdef APP-PLUSthis.isApp = true// #endifconst systemInfo = uni.getSystemInfoSync()// 状态栏高度this.statusBarHeight = systemInfo.statusBarHeight || 0// 优先使用 safeAreaInsets(新版本)if (systemInfo.safeAreaInsets) {const { top, bottom, left, right } = systemInfo.safeAreaInsetsthis.safeAreaInsetsTop = top || this.statusBarHeightthis.safeAreaInsetsBottom = bottom || 0this.safeAreaInsetsLeft = left || 0this.safeAreaInsetsRight = right || 0}// 旧版兼容 safeAreaelse if (systemInfo.safeArea) {const safeArea = systemInfo.safeAreathis.safeAreaInsetsTop = safeArea.top || this.statusBarHeightthis.safeAreaInsetsBottom = systemInfo.screenHeight - safeArea.bottomthis.safeAreaInsetsLeft = safeArea.left || 0this.safeAreaInsetsRight = systemInfo.screenWidth - safeArea.right}// 如果顶部安全区为 0,但有状态栏高度 → 用状态栏高度兜底if (!this.safeAreaInsetsTop && this.statusBarHeight) {this.safeAreaInsetsTop = this.statusBarHeight}}}
}
在页面中引入:
import safeAreaMixin from '@/mixins/safeAreaMixin.js'export default {mixins: [safeAreaMixin],// ...
}
在模板中使用:
<view class="page"><!-- 顶部安全区 --><view class="safe-top" :style="{ height: headerSafeHeight + 'px' }"></view><view class="content">页面内容</view><!-- 底部安全区 --><view class="safe-bottom" :style="{ height: bottomSafeHeight + 'px' }"></view>
</view>
四、封装一个 通用的自定义导航栏组件,适配 App / 小程序 / H5,自动处理 安全区 + 状态栏高度 + 返回按钮 + 标题 + 右侧操作按钮。
📦 1、文件结构
components/
└── CustomNavBar/
└── CustomNavBar.vue
mixins/
└── safeAreaMixin.js (前面写过这个)
🧱 2、CustomNavBar.vue 代码
<template><view class="custom-navbar"><!-- 顶部安全区 --><view :style="{ height: headerSafeHeight + 'px' }"></view><!-- 实际导航栏 --><view class="navbar" :style="navbarStyle"><!-- 左侧:返回按钮 --><view class="nav-left"><imagev-if="showBack"class="back-icon"src="/static/icon/back.svg"mode="aspectFit"@tap="handleBack"/><slot name="left"></slot></view><!-- 中间:标题 --><view class="nav-title"><text>{{ title }}</text></view><!-- 右侧:按钮或自定义插槽 --><view class="nav-right"><slot name="right"><text v-if="rightText" class="right-text" @tap="onRightTap">{{ rightText }}</text></slot></view></view></view>
</template><script>
import safeAreaMixin from '@/mixins/safeAreaMixin.js'export default {name: 'CustomNavBar',mixins: [safeAreaMixin],props: {title: {type: String,default: ''},showBack: {type: Boolean,default: true},rightText: {type: String,default: ''},bgColor: {type: String,default: '#FFFFFF'},textColor: {type: String,default: '#0D0D0D'},border: {type: Boolean,default: true}},computed: {navbarStyle() {return {backgroundColor: this.bgColor,color: this.textColor,borderBottom: this.border ? '2rpx solid #f0f0f0' : 'none'}}},methods: {handleBack() {// 如果页面栈只有1个,则返回首页const pages = getCurrentPages()if (pages.length === 1) {uni.switchTab({ url: '/pages/home/index' })} else {uni.navigateBack()}},onRightTap() {this.$emit('right-click')}}
}
</script><style scoped lang="scss">
.custom-navbar {width: 100%;
}.navbar {height: 88rpx;display: flex;align-items: center;justify-content: space-between;padding: 0 32rpx;box-sizing: border-box;
}.nav-left,
.nav-right {width: 120rpx;display: flex;align-items: center;justify-content: center;
}.nav-title {flex: 1;text-align: center;font-size: 32rpx;font-weight: 500;
}.back-icon {width: 48rpx;height: 48rpx;
}.right-text {font-size: 28rpx;color: #ff1a47;
}
</style>
🚀 3、在页面中使用
在需要的页面顶部关闭系统导航栏:
{"navigationStyle": "custom"
}
然后在页面模板中:
<template><view class="page"><CustomNavBartitle="商品詳情"rightText="分享"@right-click="onShare"/><scroll-view scroll-y class="content"><!-- 页面内容 --></scroll-view></view>
</template><script>
import CustomNavBar from '@/components/CustomNavBar/CustomNavBar.vue'export default {components: { CustomNavBar },methods: {onShare() {uni.showToast({ title: '点击了分享', icon: 'none' })}}
}
</script>
✨ 4、支持插槽扩展
还可以像这样使用自定义左右区域:
<CustomNavBar title="首頁"><template #left><image src="/static/icon/menu.svg" style="width:40rpx;height:40rpx" /></template><template #right><image src="/static/icon/search.svg" style="width:40rpx;height:40rpx" /></template>
</CustomNavBar>
