uni-app 中 web-view 的使用与 App 端全屏问题处理
web-view 是 uni-app 中用于承载网页内容的组件,常见场景包括嵌入 H5 页面、PDF 预览、外部系统页面、报表页面等。
它看起来像普通组件,但在 App 端,尤其是 Android 环境下,web-view 的表现和普通 view、image、scroll-view 很不一样。
一、web-view 是什么
web-view 本质上是一个网页容器,可以加载一个 URL。
简单用法:
html
<web-view :src="url"></web-view>
例如:
html
<web-view
src="https://example.com"
style="width: 100%; height: 100%;"
/>
它适合用来:
- 嵌入已有 H5 页面
- 打开第三方网页
- 展示 PDF.js 页面
- 展示报表、可视化页面
- 承载需要独立运行的 Web 应用
二、H5 端和 App 端的差异
在 H5 端,web-view 通常会被编译成类似 iframe 的结构,CSS 对它的控制比较直观。
但在 App 端,尤其是 App-vue 模式下,web-view 是原生 WebView 子窗口。
这意味着:
- 它不是普通 DOM 节点
- CSS 不一定能完全控制它
- 层级通常高于普通页面元素
- Android 上可能默认铺满页面
- 普通
z-index对它不一定有效
这也是很多人遇到的问题:
html
<view style="width: 300px; height: 400px;">
<web-view :src="url" />
</view>
在 H5 看起来正常,但在 Android App 端可能仍然全屏。
三、为什么 Android 上 web-view 会全屏
原因在于 App 端的 web-view 是原生层组件。
普通页面里的 view、text、image 等是 uni-app 渲染层内容,而 web-view 在 App 端会创建一个原生 WebView 窗口。
所以这类 CSS:
css
.webview-box {
width: 500px;
height: 300px;
overflow: hidden;
}
不一定能限制 Android 原生 WebView 的实际尺寸。
即使你写了:
html
<web-view
:src="url"
style="width: 100%; height: 100%;"
:fullscreen="false"
/>
在某些 App-vue / Android 场景下,也可能仍然全屏。
四、fullscreen=false 是否一定有效
不一定。
fullscreen 属性在部分平台有效,但不能把它当成 Android App-vue 端的稳定解决方案。
例如:
html
<web-view
:src="url"
:fullscreen="false"
/>
这个写法可以保留,但如果 Android 端仍然全屏,就需要进一步通过原生 WebView 设置尺寸。
五、推荐写法:容器 + webview-styles + 原生 setStyle
页面结构可以这样写:
html
<view class="webview-wrapper">
<web-view
:src="url"
:fullscreen="false"
class="inner-webview"
:webview-styles="{
width: '100%',
height: '100%'
}"
/>
</view>
CSS:
css
.webview-wrapper {
width: 100%;
height: 500px;
position: relative;
}
.inner-webview {
width: 100%;
height: 100%;
}
但在 Android App 端,如果仍然全屏,还需要同步原生 WebView 的实际位置和大小。
六、Android App 端强制限制 web-view 尺寸
核心思路:
- 用
createSelectorQuery()获取页面中容器的真实坐标。 - 获取当前页面的 App WebView。
- 获取
web-view对应的原生子 WebView。 - 调用
setStyle()设置top、left、width、height。
示例:
js
const adjustAppWebview = () => {
// #ifdef APP-PLUS
uni
.createSelectorQuery()
.select('.webview-wrapper')
.boundingClientRect((rect) => {
if (!rect || !rect.width || !rect.height) return;
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentWebview =
currentPage?.$getAppWebview?.() ||
currentPage?.$vm?.$scope?.$getAppWebview?.();
const childWebview = currentWebview?.children?.()?.[0];
if (!childWebview || typeof childWebview.setStyle !== 'function') {
return;
}
childWebview.setStyle({
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height)
});
})
.exec();
// #endif
};
然后在页面生命周期里调用:
js
import { nextTick } from 'vue';
import { onReady, onShow } from '@dcloudio/uni-app';
onReady(() => {
nextTick(() => {
adjustAppWebview();
});
});
onShow(() => {
nextTick(() => {
adjustAppWebview();
});
});
如果 WebView 创建有延迟,可以加重试:
js
const adjustAppWebview = (attempt = 0) => {
// #ifdef APP-PLUS
nextTick(() => {
setTimeout(() => {
uni
.createSelectorQuery()
.select('.webview-wrapper')
.boundingClientRect((rect) => {
if (!rect || !rect.width || !rect.height) {
if (attempt < 8) adjustAppWebview(attempt + 1);
return;
}
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentWebview =
currentPage?.$getAppWebview?.() ||
currentPage?.$vm?.$scope?.$getAppWebview?.();
const childWebview = currentWebview?.children?.()?.[0];
if (!childWebview || typeof childWebview.setStyle !== 'function') {
if (attempt < 8) adjustAppWebview(attempt + 1);
return;
}
childWebview.setStyle({
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height)
});
})
.exec();
}, attempt === 0 ? 300 : 200);
});
// #endif
};
七、资源中心 PDF 预览的处理方式
例如资源中心 PDF 预览页面:
html
<view class="app-pdf-preview">
<web-view
:src="getPdfViewerUrl"
class="pdf-webview"
:fullscreen="false"
style="width: 100%; height: 100%; border: 0"
:webview-styles="{
width: '100%',
height: '100%',
progress: { color: '#16a5fb' }
}"
/>
</view>
然后在 App 端执行:
js
const adjustAppPdfWebview = () => {
// #ifdef APP-PLUS
uni
.createSelectorQuery()
.select('.app-pdf-preview')
.boundingClientRect((rect) => {
if (!rect) return;
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentWebview = currentPage?.$getAppWebview?.();
const childWebview = currentWebview?.children?.()?.[0];
childWebview?.setStyle?.({
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height)
});
})
.exec();
// #endif
};
这样可以避免 Android 上 PDF 的 web-view 覆盖整个页面。
八、注意事项
web-view 在 App 端层级高,不建议在它上面覆盖普通 view 做按钮、弹窗或遮罩。
如果必须覆盖,优先考虑:
- 原生子窗体
subNVue- App 原生插件
- 页面外部控制按钮
不要依赖普通 CSS 的 z-index。
另外,如果一个页面里有多个 web-view,不能简单使用:
js
currentWebview.children()[0]
因为第一个子 WebView 不一定就是目标 WebView。
这种情况下需要:
- 控制页面只存在一个
web-view - 或根据子 WebView 的 URL 判断
- 或封装专门的 WebView 页面
九、最佳实践
对于 uni-app 中的 web-view,建议遵循以下原则:
- H5 端可以按普通 iframe 思路处理。
- App 端要把它当成原生窗口处理。
- Android 上不要只依赖 CSS 控制尺寸。
- 非全屏需求要配合
setStyle()。 - 页面复杂时,尽量只保留一个
web-view。 - PDF 预览推荐使用 PDF.js 页面作为
src。 - Office 文件预览优先考虑系统应用、X5 插件或后端转 PDF。
十、总结
web-view 是 uni-app 中非常实用的组件,但它不是普通组件。
在 H5 中,它像 iframe;在 App 中,它更像一个原生 WebView 窗口。
所以当 Android 上出现 web-view 全屏、遮挡页面、CSS 不生效等问题时,不能只改样式,而要通过 App 端原生 WebView 的 setStyle() 设置真实位置和尺寸。
一句话总结:
H5 用 CSS 控制,App 端用原生 WebView 样式控制。