HarmonyOS 开发之 ArkWeb 实战指南

227 阅读6分钟

HarmonyOS 开发之 ArkWeb 实战指南

源码地址:https://gitee.com/harmonyos_codelabs/SelectContact.git

一、ArkWeb 组件基础与生命周期管理

1.1 Web 组件核心能力概述

ArkWeb 的Web组件支持加载本地或在线网页,提供完整的生命周期回调体系,覆盖从组件创建到销毁的全流程。通过监听生命周期事件,开发者可精准控制网页加载流程、注入自定义逻辑、实现性能监控及资源管理。

1.2 生命周期事件详解

图片.png

1.2.1 组件初始化阶段

aboutToAppear触发时机:自定义组件实例创建后,build函数执行前。核心作用

开启调试模式:webview.WebviewController.setWebDebuggingAccess(true),配合 Chrome DevTools 调试。

配置跨域策略:通过customizeSchemes设置自定义协议的跨域权限。

初始化 Cookie:调用configCookie设置全局 Cookie 策略。

onControllerAttached触发时机Controller成功绑定到Web组件时。使用限制

禁止在此事件前调用Web组件接口,否则抛出js-error推荐操作

注入 JS 对象:registerJavaScriptProxy实现应用与前端双向通信。

设置用户代理:setCustomUserAgent自定义请求头标识。

预加载 URL:调用loadUrl初始化页面(此时不可操作网页元素,如缩放)。

1.2.2 网页加载控制

onLoadIntercept触发时机:加载 URL 前(包括loadUrliframe)。功能:全局拦截请求,返回true阻止加载,false允许。典型场景:过滤非法 URL、强制跳转登录页。

onOverrideUrlLoading触发时机:仅在iframe加载非HTTP(s)/about:blank协议时触发。onLoadIntercept区别:更细粒度控制iframe加载,支持协议白名单过滤。

onInterceptRequest触发时机:请求发起前。高级功能:拦截 URL 并返回自定义响应(如本地模拟数据),示例:

onInterceptRequest((event) => {

  const customResponse = new WebResourceResponse();

&#x20; customResponse.setResponseData('\<h1>Mocked Content\</h1>');

&#x20; customResponse.setResponseMimeType('text/html');

&#x20; return customResponse; // 用自定义数据替换原始请求

})
1.2.3 加载进度与状态

onPageBegin/onPageEnd触发范围:仅主框架(main frame),子框架(iframe)不触发。最佳实践:在onPageEnd执行 JS 脚本(如loadUrl注入动态内容),注意此时 DOM 可能未完全渲染。

onProgressChange参数event.newProgress(0-100 的加载进度)。应用场景:显示进度条,处理多框架页面的渐进式加载(主框架完成后子框架可能仍在加载)。

1.2.4 高级场景

onRenderExited触发条件:渲染进程异常退出(如内存泄漏、系统终止)。恢复策略:调用loadUrl重新加载页面,释放无效资源。

onDisAppear通用事件:组件从组件树卸载时触发,用于清理定时器、取消网络请求等。

1.3 性能监控指标

ArkWeb 支持主流性能指标回调:

onFirstContentfulPaint:首次内容绘制(文本 / 图像 / Canvas)。

onLargestContentfulPaint:最大内容绘制(可视区最大元素加载完成)。

onFirstMeaningfulPaint:首次有效绘制(页面主要内容渲染完成)。数据用途:优化首屏加载速度,定位慢资源(如延迟加载非关键图片)。

二、应用与前端双向通信

2.1 应用侧调用前端 JS 函数

通过WebviewControllerrunJavaScript系列方法执行前端代码:

2.1.1 基础用法
// 无参调用

this.webviewController.runJavaScript('htmlTest()');&#x20;

// 带参调用(字符串/JSON)

this.webviewController.runJavaScript(\`htmlTest('\${param}')\`);&#x20;

// 异步获取结果(runJavaScriptExt)

this.webviewController.runJavaScriptExt('getUserInfo()', (err, result) => {

&#x20; if (!err) console.log('JS返回值:', result);

});
2.1.2 动态注入代码
Button('修改样式').onClick(() => {

&#x20; const jsCode = \`

&#x20;   document.body.style.backgroundColor = 'lightblue';

&#x20;   document.getElementById('text').textContent = '动态修改内容';

&#x20; \`;

&#x20; this.webviewController.runJavaScript(jsCode);

});

2.2 前端调用应用侧函数(JS 代理)

通过注册 JS 代理对象,使前端可直接调用应用侧 TS 方法。

2.2.1 初始化注册(推荐)
Web({ src: \$rawfile('index.html'), controller: this.webviewController })

&#x20; .javaScriptProxy({

&#x20;   object: new MyService(), // 应用侧对象

&#x20;   name: 'appService',      // 前端调用的对象名

&#x20;   methodList: \['fetchData', 'logInfo'], // 允许调用的方法列表

&#x20;   permission: {            // 权限配置(可选,精确匹配URL)

&#x20;     urlPermissionList: \[{ scheme: 'resource', host: 'rawfile' }]

&#x20;   }

&#x20; })
2.2.2 动态注册 / 销毁
// 动态注册(需调用refresh生效)

this.webviewController.registerJavaScriptProxy(service, 'appService', \['fetchData']);

this.webviewController.refresh();

// 销毁代理(防止内存泄漏)

this.webviewController.deleteJavaScriptRegister('appService');
2.2.3 前端调用示例
\<script>

&#x20; // 调用应用侧方法

&#x20; async function loadData() {

&#x20;   const result = await appService.fetchData('param'); // 异步调用(需方法返回Promise)

&#x20;   document.getElementById('data').textContent = result;

&#x20; }

\</script>

2.3 复杂数据类型传递

2.3.1 数组 / 对象传递
// 应用侧返回数组

class DataService {

&#x20; getNumbers(): number\[] { return \[1, 2, 3, 4]; }

}

// 前端接收(自动转为JS数组)

const numbers = appService.getNumbers(); // \[1,2,3,4]
2.3.2 回调函数传递
// 应用侧接收前端回调

class CallbackService {

&#x20; process(callback: (data: string) => void) {

&#x20;   setTimeout(() => callback('异步结果'), 1000);

&#x20; }

}

// 前端传递箭头函数

appService.process((result) => {

&#x20; alert('回调结果:', result); // 1秒后弹出

});
2.3.3 Promise 场景
// 应用侧返回Promise

class AsyncService {

&#x20; fetchUser(): Promise\<User> {

&#x20;   return new Promise((resolve) => {

&#x20;     resolve({ id: 1, name: 'John' });

&#x20;   });

&#x20; }

}

// 前端使用async/await

async function getUser() {

&#x20; const user = await appService.fetchUser(); // 等待Promise解析

&#x20; console.log(user.name); // John

}

三、实战示例与最佳实践

3.1 自定义协议拦截(本地数据模拟)

// 拦截以'mock://'开头的URL

onLoadIntercept((event) => {

&#x20; const url = event.data.getRequestUrl();

&#x20; if (url.startsWith('mock://user')) {

&#x20;   const mockData = \`

&#x20;     { "users": \[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}] }

&#x20;   \`;

&#x20;   const response = new WebResourceResponse();

&#x20;   response.setResponseData(mockData);

&#x20;   response.setResponseMimeType('application/json');

&#x20;   event.data.respondWith(response); // 用模拟数据响应

&#x20;   return true; // 阻止原始请求

&#x20; }

&#x20; return false;

});

3.2 性能监控仪表盘

onFirstContentfulPaint((event) => {

&#x20; this.perfMetrics.fcp = event.firstContentfulPaintMs;

});

onLargestContentfulPaint((event) => {

&#x20; this.perfMetrics.lcp = event.largestContentfulPaintMs;

&#x20; this.updatePerfReport(); // 更新UI展示

});

3.3 安全最佳实践

权限最小化

仅注册必要方法:methodList限制可调用的 TS 方法。

URL 白名单:通过urlPermissionList限制代理方法仅在特定域名生效。

防止 XSS 攻击

对前端传入的 HTML 内容进行转义,避免动态执行恶意脚本。

资源释放

onDisAppear中销毁 JS 代理:deleteJavaScriptRegister

监听onRenderExited,重新加载页面恢复状态。

四、开发工具与调试技巧

开启调试模式

aboutToAppear() {

&#x20; webview.WebviewController.setWebDebuggingAccess(true); // 允许Chrome DevTools调试

}

连接设备后,通过 Chrome 浏览器访问chrome://inspect查看实时网页调试信息。

日志监控

生命周期事件中添加console.log,通过 HarmonyOS DevEco Studio 的 Log 窗口跟踪流程。

前端 JS 错误可通过onRenderExited捕获渲染进程异常。

网络抓包

使用 Charles/Fiddler 等工具,结合setCustomUserAgent标识请求来源,分析网络请求是否符合预期。

五、常见问题与解决方案

问题描述可能原因解决方案
JS 代理调用失败权限配置错误检查urlPermissionList是否匹配当前 URL 协议 / 主机
网页空白无内容资源路径错误使用$rawfile('path')加载本地文件,确保路径正确
跨域请求失败未配置跨域策略aboutToAppear中调用customizeSchemes添加允许的协议
性能回调未触发页面无有效内容确保页面包含文本、图像等可绘制元素

六、总结

ArkWeb 组件通过完善的生命周期控制和灵活的双向通信机制,实现了 HarmonyOS 应用与 Web 技术的深度融合。开发者可基于此构建混合开发应用,兼顾原生性能与 Web 开发效率。在实际项目中,需注意权限安全、资源释放及性能优化,充分利用调试工具提升开发效率。