App端uni.showLoading不生效因其调用原生弹窗,样式不可定制且无法覆盖状态栏;需自建v-show控制的全局Loading组件,通过provide/inject或挂载到uni.$loading实现跨文件调用。

uni-app App端自定义Loading遮罩为什么uni.showLoading不生效
因为uni.showLoading在App端(iOS/Android)默认使用原生弹窗,样式不可定制、无法覆盖原生导航栏或状态栏,且不支持透明背景、自定义动画、点击穿透等需求。真机调试时你看到的“白底黑字转圈”就是它,改不了。
真正能控制样式的,只有自己用view + v-if或v-show实现的浮层——本质是页面内的DOM节点,不是API调用。
- App端
uni.showLoading和uni.hideLoading只适合简单提示,别指望它配合你的UI系统 - 如果你用了
uni.showToast或uni.showModal混搭遮罩,会触发多层原生弹窗冲突,导致卡死或无法关闭 - H5和小程序端
uni.showLoading样式可部分覆盖,但App端完全不走CSS渲染路径,改CSS无效
怎么写一个App可用的全局Loading组件(Vue 3 + Composition API)
核心思路:用provide/inject注入控制函数,组件内用v-show控制显隐(不用v-if避免重复挂载开销),遮罩层级必须设为z-index: 9999并加position: fixed。
示例精简版(components/GlobalLoading.vue):
<template><view class="loading-overlay" v-show="visible"><view class="loading-content"><text class="loading-text">{{ text }}</text></view></view></template><script setup>
import { ref, onMounted } from 'vue'
const visible = ref(false)
const text = ref('加载中...')
const show = (msg = '加载中...') => {
text.value = msg
visible.value = true
}
const hide = () => {
visible.value = false
}
// 注入到app.vue根作用域
defineExpose({ show, hide })
</script><style scoped>
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
.loading-content {
padding: 20rpx 40rpx;
background: #000;
border-radius: 8rpx;
text-align: center;
}
.loading-text {
color: #fff;
font-size: 28rpx;
}
</style>
- 必须用
v-show,App端v-if切换时可能触发渲染异常或白屏 -
z-index要够高,App端WebView对层级计算更敏感,999不够,得9999 - 不要在
show()里加setTimeout模拟延迟——真实请求应该由业务逻辑控制显示时机 - 如果项目用了
uni.navigateTo跳转,需确保新页面也引入该组件并接收inject,否则遮罩不会跟随路由消失
如何在任意页面/JS模块里调用这个Loading(包括utils/里的请求封装)
关键在app.vue里把实例方法挂到globalProperties,让uni.$loading成为全局可用对象。不能只靠import组件再ref,那样跨文件调用不现实。
在app.vue的setup中:
import GlobalLoading from '@/components/GlobalLoading.vue'
export default {
components: { GlobalLoading },
setup() {
const loadingRef = ref(null)
// 暴露方法给全局
const $loading = reactive({
show: (msg) => loadingRef.value?.show(msg),
hide: () => loadingRef.value?.hide()
})
getCurrentInstance().appContext.config.globalProperties.$loading = $loading
return () => (
<view><globalloading ref="{loadingRef}"></globalloading><routerview></routerview></view>
)
}
}
之后 anywhere 都可以:
- 在页面中:
this.$loading.show('提交中')或uni.$loading.show('验证中') - 在
utils/request.js里:uni.$loading.show()+try/catch/finally中uni.$loading.hide() - 注意:H5端需额外处理
document.body滚动锁定(App端WebView自动处理了,不用管) - 如果用了Pinia,不建议把Loading状态放store里——它本质是副作用,不是业务状态
App端真机调试时Loading闪一下就消失?常见原因
不是代码问题,是生命周期或异步时机错位。App端WebView对mounted、onReady响应比H5慢,遮罩若在onLoad里立刻show,可能DOM还没渲染完就被hide了。
- 请求发得太早:比如在
onLoad第一行就uni.$loading.show(),紧接着uni.request,但网络极快时回调同步执行,hide紧随其后,人眼看不到 - 没处理
onPullDownRefresh场景:下拉刷新时遮罩被uni.stopPullDownRefresh()干扰,建议单独用isRefreshing标志位隔离 - Android低端机上
opacity动画卡顿,导致遮罩“突现突隐”,换成visibility: hidden/visible更稳 - 字体图标(如uni-icons)在App端未正确引入,导致
loading-content高度塌陷,遮罩看起来“没出来”
最稳妥的做法:所有show都包一层nextTick,哪怕只是防个万一。










