本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、基础排查:权限与网络状态

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工具进行前端调试。主要检查以下几个方面:

  1. 元素检查:HTML元素是否完整,结构是否正确

  2. 控制台错误:查看JS错误、MixedContent策略、CORS策略等异常

  3. 网络请求:检查资源加载时间、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);

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐