更新记录

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)

  1. uni.getMenuButtonBoundingClientRect() 只有当前页面是原生导航栏才返回正确值。如果页面设置 navigationStyle: custom 自定义导航,胶囊信息正常获取;如果页面隐藏导航栏但没有设置 custom,胶囊可能返回 0。

  2. 小程序切换 tab 页面不会触发 onUnload,所以组件的 beforeDestroy 不会执行(tab 页面本身不会销毁),属于小程序原生机制,不是插件 bug。Tab 页建议:tab 页面不要依赖 destroy,页面 onShow 会刷新安全区数据,不影响使用。

  3. 部分低版本微信基础库,safeArea 字段不存在,会默认返回 0,不会报错。

支付宝小程序(MP-ALIPAY)

  1. 支付宝小程序没有胶囊按钮接口getMenuButtonBoundingClientRect 会抛异常,插件内部已经 try-catch 捕获,menuButtonInfo 保持 0,不会崩溃。支付宝自定义导航不能用 direction="menu-top",只能手动写固定高度。

  2. 支付宝 sysInfo.safeArea 字段存在,但部分老机型返回值不准,建议兜底判断。

App 端(APP-PLUS)

  1. nvue 页面不支持 CSS 变量,也不支持 H5 那种动态 style 注入。nvue 页面不能使用 class 方式,只能使用 <uni-safe-area-view> 组件或者 JS 读取数据写样式。

  2. plus.globalEvent 横竖屏监听:App 在后台切回前台不会自动触发 orientationchange;从后台切回 App,页面 onShow 会刷新安全区,弥补这个缺陷。

  3. App 热更新重启后,plus 对象需要等 plus ready,插件在 onShow 刷新,一般没问题;极少数场景 plus 未初始化,safeArea 值为 0,加延时可兜底。

鸿蒙端(APP-HARMONY)

  1. 鸿蒙安全区数据通过 uni.getSystemInfoSync().safeArea 获取,与 APP-PLUS 计算方式一致。

  2. 鸿蒙底部手势条高度因设备不同而异(约 20~34px),插件自动获取,无需手动配置。

  3. 鸿蒙不支持 plus.globalEvent,横竖屏监听不生效,需依赖页面 onShow 手动刷新。

H5 端

  1. 浏览器刘海屏(手机浏览器)的 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 最常见大坑。

  1. H5 单页路由切换(hash/history 模式):页面 onUnload 正常执行,样式会移除,切页自动重建样式。

  2. 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 不会触发

onShowonUnload页面级生命周期,子组件中不会触发。组件内部使用 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

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议