鸿蒙 Web组件白屏问题
本文摘要:文章详细介绍了Web组件白屏问题的排查方法,包括基础权限配置(网络权限、DOM存储等)、网络状态检查、UserAgent优化等。重点分析了跨域资源访问的两种解决方案:资源拦截映射和设置跨域访问路径。同时讲解了DevTools调试技巧、错误回调接口使用、渲染模式选择、布局问题处理以及内存监控要点。针对多进程设备的白屏问题,建议强制单进程模式加载。文章提供了完整的代码示例和解决方案,帮助开发
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、基础排查:权限与网络状态
1.1 网络权限配置
Web组件加载在线页面必须配置网络权限。在module.json5文件中添加权限声明:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
1.2 Web组件关键权限设置
在Web组件初始化时,需要正确配置以下权限:
| 权限名称 | 说明 | 作用 |
|---|---|---|
| domStorageAccess | DOM Storage API权限 | 若不开启,无法使用localStorage存储数据,依赖本地存储的功能会异常 |
| fileAccess | 文件访问权限 | 若不开启,文件读写功能完全被阻断,依赖文件的模块会崩溃 |
| imageAccess | 图片加载权限 | 设置是否允许自动加载图片资源 |
| onlineImageAccess | 网络图片加载权限 | 设置是否允许从网络加载图片资源(HTTP/HTTPS) |
| javaScriptAccess | JavaScript执行权限 | 设置是否允许执行JavaScript脚本 |
代码示例:
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({ src: 'www.example.com', controller: this.controller })
.domStorageAccess(true)
.fileAccess(true)
.imageAccess(true)
.onlineImageAccess(true)
.javaScriptAccess(true)
}
}
}
1.3 网络状态
在排查白屏问题前,首先确认:
-
设备是否已连接网络
-
设备自带浏览器能否正常访问网页
-
目标页面是否为在线可访问状态
1.4 UserAgent配置优化
某些网站会根据UserAgent进行适配,修改UserAgent可能解决页面适配问题:
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
@State customUserAgent: string = ' DemoApp';
build() {
Column() {
Web({ src: 'www.example.com', controller: this.controller })
.onControllerAttached(() => {
console.info('onControllerAttached');
try {
let userAgent = this.controller.getUserAgent() + this.customUserAgent;
this.controller.setCustomUserAgent(userAgent);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
}
}
}
二、使用DevTools进行深度调试
当权限配置无误但仍出现白屏时,应使用DevTools工具进行前端调试。主要检查以下几个方面:
-
元素检查:HTML元素是否完整,结构是否正确
-
控制台错误:查看JS错误、MixedContent策略、CORS策略等异常
-
网络请求:检查资源加载时间、404错误等
2.2 常见错误类型
CORS跨域错误示例:
Access to script at 'xxx' from origin 'xxx' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes: http, arkweb, data,
chrome-extension, chrome, https, chrome-untrusted.
2.3 本地资源跨域解决方案
ArkWeb内核禁止file协议和resource协议访问跨域请求,有两种解决方案:
方法一:HTTP/HTTPS协议替代+资源拦截
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct Index {
webviewController: webview.WebviewController = new webview.WebviewController();
// 构造域名和本地文件的映射表
schemeMap = new Map([
["https://www.example.com/index.html", "index.html"],
["https://www.example.com/js/script.js", "js/script.js"],
])
// 构造本地文件和mimeType映射
mimeTypeMap = new Map([
["index.html", 'text/html'],
["js/script.js", "text/javascript"]
])
build() {
Row() {
Column() {
Web({ src: "https://www.example.com/index.html", controller: this.webviewController })
.javaScriptAccess(true)
.fileAccess(true)
.domStorageAccess(true)
.geolocationAccess(true)
.width("100%")
.height("100%")
.onInterceptRequest((event) => {
if (!event) {
return;
}
if (this.schemeMap.has(event.request.getRequestUrl())) {
let rawfileName: string = this.schemeMap.get(event.request.getRequestUrl())!;
let mimeType = this.mimeTypeMap.get(rawfileName);
if (typeof mimeType === 'string') {
let response = new WebResourceResponse();
response.setResponseData($rawfile(rawfileName));
response.setResponseEncoding('utf-8');
response.setResponseMimeType(mimeType);
response.setResponseCode(200);
response.setReasonMessage('OK');
response.setResponseIsReady(true);
return response;
}
}
return null;
})
}
.width('100%')
}
.height('100%')
}
}
方法二:setPathAllowingUniversalAccess配置
通过设置允许跨域访问的路径列表,使file协议可以跨域访问指定目录下的资源。
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct WebComponent {
controller: WebviewController = new webview.WebviewController();
uiContext: UIContext = this.getUIContext();
build() {
Row() {
Web({ src: '', controller: this.controller })
.onControllerAttached(() => {
try {
// 设置允许跨域访问的路径列表
this.controller.setPathAllowingUniversalAccess([
this.uiContext.getHostContext()!.resourceDir,
this.uiContext.getHostContext()!.filesDir + '/example'
])
this.controller.loadUrl('file://' + this.uiContext.getHostContext()!.resourceDir + '/index.html')
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
.javaScriptAccess(true)
.fileAccess(true)
.domStorageAccess(true)
}
}
}
支持的路径格式:
| 目录类型 | 获取方式 | 示例路径 |
|---|---|---|
| 应用文件目录 | Context.filesDir | /data/storage/el2/base/files/example |
| 应用资源目录 | Context.resourceDir | /data/storage/el1/bundle/entry/resources/resfile |
| 应用缓存目录(API 21+) | Context.cacheDir | /data/storage/el2/base/cache |
| 应用临时目录(API 21+) | Context.tempDir | /data/storage/el2/base/temp |
注意:设置的目录路径中,不允许包含cache/web,否则会抛出异常码401。
三、错误上报接口排查
3.1 关键错误回调接口
| 接口名称 | 触发条件 | 排查方向 |
|---|---|---|
| onErrorReceive | 资源加载失败 | 检查访问内核不支持的scheme(如302 UNKNOWN_URL_SCHEME) |
| onHttpErrorReceive | 服务器返回HTTP错误码 | 需要与服务器联调 |
| onHttpAuthRequest | 服务器返回407需要认证 | 正确处理用户名密码认证 |
| onClientAuthenticationRequest | 服务器请求客户端证书 | 正确配置证书 |
| onSslErrorEvent | 证书错误 | 检查证书是否过期或配置错误 |
四、布局与渲染模式问题
4.1 渲染模式限制
Web组件提供两种渲染模式:
异步渲染模式(Async Render)
-
宽高不能超过7680px(物理像素)
-
超过限制会导致白屏
同步渲染模式(Sync Render)
-
适用于需要自适应页面布局的场景
-
需要正确配置相关属性
4.2 自适应页面布局约束
配置自适应页面布局时需注意:
Web({ src: 'www.example.com', controller: this.controller })
.webSetting({
renderingMode: WebRenderingMode.SYNCHRONOUS, // 同步渲染模式
overScrollMode: OverScrollMode.NEVER // 关闭滚动效果
})
约束条件:
-
不支持动态调整组件高度,确保页面高度固定
-
避免在FIT_CONTENT模式下启用键盘避让属性RESIZE_CONTENT
-
CSS样式height: xxx vh可能引发计算冲突
4.3 vh单位导致的布局问题
问题场景:
<body>
<div id="1">
<div id="2" style="height: 100vh">子dom</div> <!-- 高度计算异常 -->
<div id="3" style="height: 20px">子dom</div>
</div>
</body>
解决方案:
方案一:子元素使用具体高度撑开父元素
<body>
<div id="1">
<div id="2">
<div style="height: 20px">子dom</div>
</div>
<div id="3" style="height: 20px">子dom</div>
</div>
</body>
方案二:父元素使用实际高度样式
<body>
<div id="1">
<div id="2" style="height: 20px">子dom</div>
<div id="3" style="height: 20px">子dom</div>
</div>
</body>
五、H5代码兼容性问题
5.1 特殊协议拦截
当H5页面调用特殊协议(如tel:、mailto:)导致白屏时,需要通过onInterceptRequest拦截并调用系统能力:
.onInterceptRequest((event) => {
if (event.request.url.startsWith('tel:')) {
// 调用系统拨号能力
call.makeCall({ phoneNumber: '123456' });
return { responseCode: 404 }; // 阻止默认行为
}
return null;
})
六、内存与生命周期监控
6.1 关键日志关键字
| 日志关键字 | 说明 | 影响 |
|---|---|---|
| StartRenderProcess failed | 渲染render进程启动失败 | 直接导致白屏 |
| MEMORY_PRESSURE_LEVEL_CRITICAL | 整机内存压力达到阈值 | 可能导致黑屏、花屏、白屏 |
| crashpad SandboxedHandler::HandlerCrash | 渲染render进程崩溃 | 白屏、Web组件卡死 |
| SharedContextState context lost via Skia OOM | 共享内存不足 | 应用闪退、花屏、卡死 |
| CreateNativeViewGLSurfaceEGLOhs::normal | 创建egl surface成功 | 没有该日志则白屏 |
| INFO: request had no response within 5 seconds | 网络超时 | 页面加载失败 |
| final url: xxx, error_code xxx | 主资源加载报错 | 页面加载失败 |
6.2 内存问题排查
当出现以下情况时,需排查内存泄漏:
-
日志中出现"MEMORY_PRESSURE_LEVEL_CRITICAL"
-
页面出现黑屏、花屏、闪屏等异常
-
Web组件频繁崩溃
七、多进程加载问题
7.1 问题
同一Web页面在手机上显示正常,但在平板/PC/2in1设备上白屏。
7.2 原因分析
平板/PC/2in1设备默认采用多进程加载模式:
-
iframe默认使用子进程加载
-
主进程加载完成后,若子进程尚未加载完成,导致白屏
7.3 解决方案
强制设置为单进程加载模式:
webview.WebviewController.setRenderProcessMode(webview.RenderProcessMode.SINGLE);
更多推荐


所有评论(0)