更新记录
1.0.0(2026-09-17) 下载此版本
完整版本
平台兼容性
uni-app(3.8.4)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
uni-app x(3.8.4)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
gibbs-safe-area v1.0.0
uni-app 全端智能安全区适配插件,支持安全区、胶囊导航、横竖屏监听、页面黑白名单、调试模式、自动销毁防泄漏
功能清单
- ✅ JS 获取 safeArea 信息(top / bottom / left / right)
- ✅ 全局注入 CSS 变量
--safe-area-top/--safe-area-bottom/--safe-area-left/--safe-area-right - ✅ H5 端快捷 class
.safe-area-bottom/.safe-area-top/.safe-area-all - ✅ 内置
<uni-safe-area-view>组件,自动填充安全区域 - ✅ 支持自定义附加高度(如
safe-area-bottom + 45px) - ✅ 小程序胶囊按钮适配(自定义导航栏)
- ✅ 横竖屏 & 窗口缩放监听,实时重算安全区
- ✅ 黑白名单页面过滤,指定页面禁用安全区
- ✅ CSS 注入开关,可关闭全局样式污染
- ✅ 全自动销毁,页面卸载清除监听,0 内存泄漏
- ✅ 完整 Debug 调试日志
- ✅ 兼容 Vue 2 / Vue 3
- ✅ 全端兼容:App(iOS/Android/鸿蒙) / 小程序 / H5
目录结构
uni_modules/gibbs-safe-area/
├── package.json
├── uni-safe-area.js # 核心逻辑
├── uni-safe-area-view.vue # 安全区填充组件
└── readme.md
安装
将 gibbs-safe-area 目录放入项目的 uni_modules/ 下即可。
全局注册
main.js(Vue 2)
import Vue from 'vue'
import uniSafeArea from '@/uni_modules/gibbs-safe-area/uni-safe-area.js'
Vue.use(uniSafeArea, {
blackList: ['pages/login/login'],
enableWhiteList: false,
whiteList: [],
injectCssVar: true,
debug: false
})
main.js(Vue 3)
import { createSSRApp } from 'vue'
import uniSafeArea from '@/uni_modules/gibbs-safe-area/uni-safe-area.js'
const app = createSSRApp(App)
app.use(uniSafeArea, {
blackList: ['pages/login/login'],
debug: false
})
配置项
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| blackList | Array | [] |
黑名单页面路由,这些页面不启用安全区 |
| enableWhiteList | Boolean | false |
是否开启白名单模式 |
| whiteList | Array | [] |
白名单页面路由,仅这些页面启用安全区 |
| injectCssVar | Boolean | true |
是否注入全局 CSS 变量和快捷 class |
| debug | Boolean | false |
调试模式,上线前务必关闭 |
使用方式
方式一:JS 获取安全区数据
import { getSafeArea } from '@/uni_modules/gibbs-safe-area/uni-safe-area.js'
const result = getSafeArea()
console.log(result.safeArea) // { top, bottom, left, right }
console.log(result.menuButton) // { top, height, width, right }
方式二:全局属性(需先 Vue.use 安装)
// Vue 2
export default {
mounted() {
console.log(this.$safeArea) // { top, bottom, left, right }
console.log(this.$menuButton) // { top, height, width, right }
}
}
// Vue 3(需 getCurrentInstance)
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
console.log(proxy.$safeArea)
方式三:CSS 变量(仅 H5 端)
.tab-bar {
padding-bottom: calc(var(--safe-area-bottom, 0px) + 45px);
}
.nav-bar {
padding-top: var(--safe-area-top, 0px);
}
方式四:快捷 class(仅 H5 端)
<view class="safe-area-bottom">底部安全区</view>
<view class="safe-area-top">顶部安全区</view>
<view class="safe-area-all">上下全部适配</view>
方式五:组件 <uni-safe-area-view>(全端通用,推荐)
<template>
<!-- 底部安全区 + 额外 45px -->
<uni-safe-area-view direction="bottom" :extra="45">
<view class="tab-bar">底部导航</view>
</uni-safe-area-view>
<!-- 顶部安全区(刘海屏) -->
<uni-safe-area-view direction="top">
<view class="nav-bar">顶部导航</view>
</uni-safe-area-view>
<!-- 自定义导航栏适配胶囊(微信小程序) -->
<uni-safe-area-view direction="menu-top" :extra="10">
<view>自定义导航栏内容</view>
</uni-safe-area-view>
<!-- 四个方向全部填充 -->
<uni-safe-area-view direction="all">
<view class="content">内容</view>
</uni-safe-area-view>
<!-- 左侧/右侧安全区 -->
<uni-safe-area-view direction="left">
<view>内容</view>
</uni-safe-area-view>
</template>
<script>
import uniSafeAreaView from '@/uni_modules/gibbs-safe-area/uni-safe-area-view.vue'
export default {
components: { uniSafeAreaView }
}
</script>
页面 onShow 手动刷新
插件不会自动劫持页面生命周期,需要在页面 onShow 中手动调用刷新:
export default {
onShow() {
this.$safeAreaRefresh && this.$safeAreaRefresh()
}
}
API
getSafeArea()
返回值:
| 属性 | 类型 | 说明 |
|---|---|---|
| safeArea | Object | 安全区信息 { top, bottom, left, right } |
| menuButton | Object | 胶囊按钮信息 { top, height, width, right } |
safeArea 字段:
| 属性 | 类型 | 说明 |
|---|---|---|
| top | Number | 顶部安全区高度(刘海/状态栏) |
| bottom | Number | 底部安全区高度(小黑条/手势条) |
| left | Number | 左侧安全区宽度 |
| right | Number | 右侧安全区宽度 |
uni-safe-area-view 组件 Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
| direction | String | 'bottom' |
填充方向:top / bottom / left / right / all / menu-top |
| extra | Number | 0 |
安全区基础上额外增加的像素高度 |
CSS 变量(仅 H5)
| 变量 | 说明 |
|---|---|
--safe-area-top |
顶部安全区高度 |
--safe-area-bottom |
底部安全区高度 |
--safe-area-left |
左侧安全区宽度 |
--safe-area-right |
右侧安全区宽度 |
--menu-top |
胶囊按钮顶部距离 |
--menu-height |
胶囊按钮高度 |
全局方法
| 方法 | 说明 |
|---|---|
this.$safeAreaRefresh() |
手动刷新安全区数据(页面 onShow 调用) |
this.$safeAreaDestroy() |
全局销毁,清除所有监听(仅 App 退出时调用) |
各端安全区来源
| 平台 | top | bottom | 说明 |
|---|---|---|---|
| iOS App | safeArea.top | screenHeight - safeArea.bottom | 刘海 + 小黑条 |
| Android App | safeArea.top | screenHeight - safeArea.bottom | 部分异形屏 |
| 鸿蒙 App | safeArea.top | screenHeight - safeArea.bottom | 手势条 |
| 微信小程序 | safeArea.top | screenHeight - safeArea.bottom | 刘海 + 小黑条 |
| 支付宝小程序 | safeArea.top | screenHeight - safeArea.bottom | 刘海 + 小黑条 |
| H5 | safeArea.top | screenHeight - safeArea.bottom | 需 viewport-fit=cover |
实际应用示例
替代 tabbar 硬编码 padding
<!-- 修改前 -->
<view style="padding-bottom: 34px;">
<view class="tab-bar">底部导航</view>
</view>
<!-- 修改后 -->
<uni-safe-area-view direction="bottom">
<view class="tab-bar">底部导航</view>
</uni-safe-area-view>
固定定位元素贴底
<template>
<view class="fixed-bottom" :style="{ paddingBottom: safeBottom + 'px' }">
内容
</view>
</template>
<script>
import { getSafeArea } from '@/uni_modules/gibbs-safe-area/uni-safe-area.js'
export default {
data() {
return { safeBottom: 0 }
},
mounted() {
this.safeBottom = getSafeArea().safeArea.bottom
}
}
</script>
微信小程序自定义导航栏
<uni-safe-area-view direction="menu-top" :extra="10">
<view class="custom-nav">
<text>页面标题</text>
</view>
</uni-safe-area-view>
使用注意事项 + 已知坑点
一、基础注意事项
1. 仅 uni_modules 目录结构
插件必须放在项目根目录 uni_modules/gibbs-safe-area/,uni-app 才会识别为 uni_module 插件。不要放到 components,否则自动引入、依赖声明会失效。
2. 注册位置 main.js / main.ts
- Vue 2:直接
Vue.use没问题 - Vue 3(Vite 版):
app.use逻辑不变,但globalProperties取值方式变了
Vue 3 不能直接 this.$safeArea,需要用 getCurrentInstance() 获取:
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
console.log(proxy.$safeArea)
3. 上线前务必关闭 debug
Vue.use(uniSafeArea, {
debug: false // 打包上线改成 false,关闭控制台大量日志
})
4. 黑白名单路由必须和 getCurrentPages 返回路由完全一致
getCurrentPages 拿到的路由不带 / 开头,例如 pages/index/index
- ❌ 错误:
/pages/index/index - ✅ 正确:
pages/index/index
写错路由,黑白名单不会生效。
5. CSS 变量注入仅 H5 端生效
--safe-area-top、.safe-area-top 这类 class 只在 H5 可用。
小程序 / App 端不支持全局注入 style,只能用
<uni-safe-area-view>组件或者 JS 读取$safeArea写内联样式。这是最大坑! 很多人以为全端都能用 class,实际不是。
6. 页面 onShow 需手动刷新
插件不会自动劫持页面生命周期,需要在页面 onShow 中手动调用:
export default {
onShow() {
this.$safeAreaRefresh && this.$safeAreaRefresh()
}
}
二、各端坑点 & 兼容说明
微信小程序(MP-WEIXIN)
-
uni.getMenuButtonBoundingClientRect()只有当前页面是原生导航栏才返回正确值。如果页面设置navigationStyle: custom自定义导航,胶囊信息正常获取;如果页面隐藏导航栏但没有设置 custom,胶囊可能返回 0。 -
小程序切换 tab 页面不会触发 onUnload,所以组件的
beforeDestroy不会执行(tab 页面本身不会销毁),属于小程序原生机制,不是插件 bug。Tab 页建议:tab 页面不要依赖 destroy,页面 onShow 会刷新安全区数据,不影响使用。 -
部分低版本微信基础库,
safeArea字段不存在,会默认返回 0,不会报错。
支付宝小程序(MP-ALIPAY)
-
支付宝小程序没有胶囊按钮接口,
getMenuButtonBoundingClientRect会抛异常,插件内部已经 try-catch 捕获,menuButtonInfo保持 0,不会崩溃。支付宝自定义导航不能用direction="menu-top",只能手动写固定高度。 -
支付宝
sysInfo.safeArea字段存在,但部分老机型返回值不准,建议兜底判断。
App 端(APP-PLUS)
-
nvue 页面不支持 CSS 变量,也不支持 H5 那种动态 style 注入。nvue 页面不能使用 class 方式,只能使用
<uni-safe-area-view>组件或者 JS 读取数据写样式。 -
plus.globalEvent横竖屏监听:App 在后台切回前台不会自动触发orientationchange;从后台切回 App,页面 onShow 会刷新安全区,弥补这个缺陷。 -
App 热更新重启后,plus 对象需要等 plus ready,插件在 onShow 刷新,一般没问题;极少数场景 plus 未初始化,safeArea 值为 0,加延时可兜底。
鸿蒙端(APP-HARMONY)
-
鸿蒙安全区数据通过
uni.getSystemInfoSync().safeArea获取,与 APP-PLUS 计算方式一致。 -
鸿蒙底部手势条高度因设备不同而异(约 20~34px),插件自动获取,无需手动配置。
-
鸿蒙不支持
plus.globalEvent,横竖屏监听不生效,需依赖页面 onShow 手动刷新。
H5 端
- 浏览器刘海屏(手机浏览器)的
safeArea依赖 viewport meta。H5 页面必须包含这个 meta 标签,否则 safeArea.bottom 拿不到小黑条高度!
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
缺少
viewport-fit=cover,H5 安全区直接失效!这是 H5 最常见大坑。
-
H5 单页路由切换(hash/history 模式):页面 onUnload 正常执行,样式会移除,切页自动重建样式。
-
PC 端浏览器没有刘海,safeArea 全部返回 0,不会报错。
三、插件本身代码的限制 & 已知问题
1. 全局挂载的 $safeArea 是引用对象,不是响应式
⚠️ 重点:this.$safeArea 不是 Vue 响应式数据,模板里直接写 :style="{paddingTop:$safeArea.top}" 不会自动更新!
两种解决办法:
- 使用
<uni-safe-area-view>组件(内部自带刷新,推荐) - 页面自己用 ref/reactive 包装一份,每次 onShow 重新赋值
export default {
data() {
return { safeBottom: 0 }
},
onShow() {
var result = getSafeArea()
this.safeBottom = result.safeArea.bottom
}
}
2. 多页面快速连续跳转
可能出现多次重复创建 style 标签。插件自带 id 去重(__gibbs_safe_area_style),不会重复插入多个 style,已经处理。
3. destroy 是全局销毁,不要随便在业务页面手动调用
this.$safeAreaDestroy() 是全局清理事件监听,调用后整个插件停止工作,只适合 App 退出、页面完全销毁场景。
4. 组件 onShow / onUnload 不会触发
onShow 和 onUnload 是页面级生命周期,子组件中不会触发。组件内部使用 beforeDestroy(Vue 2)/ beforeUnmount(Vue 3)来清理监听。
四、推荐最佳实践
| 场景 | 推荐方式 |
|---|---|
| H5 页面 | 优先用 .safe-area-bottom 等 class,简单省事;记得加 viewport-fit=cover |
| 小程序 / App 页面 | 优先使用 <uni-safe-area-view> 组件,兼容性最好 |
| 自定义导航栏(微信小程序) | <uni-safe-area-view direction="menu-top" :extra="10"> |
| 底部 Tab 栏 | <uni-safe-area-view direction="bottom" :extra="45"> |
| nvue 页面 | 只用组件,不要用 class |
| 固定定位贴底 | JS 获取 getSafeArea().safeArea.bottom 写内联样式 |
五、快速排错清单
出问题按顺序排查:
| 现象 | 排查方向 |
|---|---|
| 安全区数值全部是 0 | 小程序:检查是否真机,模拟器 safeArea 经常为 0;H5:检查 meta viewport 是否带 viewport-fit=cover;App:使用真机测试,模拟器 safeArea 数据不准 |
| 页面切换后安全区不变 | 检查页面 onShow 是否调用了 this.$safeAreaRefresh();检查路由黑白名单配置,是否被加入黑名单 |
| H5 class 样式不生效 | 确认 injectCssVar: true;确认页面不在黑名单内;打开 F12,看 head 有没有 #__gibbs_safe_area_style style 标签 |
| 控制台大量 log | 打包前 debug 设置 false |
Vue 3 取不到 $safeArea |
Vue 3 需用 getCurrentInstance().proxy.$safeArea,不能直接 this.$safeArea |
| 鸿蒙安全区为 0 | 确认鸿蒙设备已开启手势条;使用真机测试 |
| 胶囊信息全为 0 | 仅微信小程序支持;确认页面设置了 navigationStyle: custom |

收藏人数:
下载插件并导入HBuilderX
下载插件ZIP
赞赏(0)
下载 4
赞赏 0
下载 12611168
赞赏 1949
赞赏
京公网安备:11010802035340号