(34))
在 HarmonyOS 的 ArkUI 开发中Web 组件是实现混合开发Hybrid的核心。通过 Web 组件开发者可以在应用内无缝嵌入网页并通过 JSBridge 实现原生端ArkTS与前端H5的双向通信。以下是 Web 组件集成与 JS 交互一、 Web 组件的四种加载方式Web 组件支持多种数据来源以适应不同的业务场景加载在线 URLWeb({ src: https://... })适用于加载网络页面。注意需在module.json5中申请ohos.permission.INTERNET权限。加载本地 rawfileWeb({ src: $rawfile(index.html) })适用于加载打包在src/main/resources/rawfile/目录下的本地 HTML 文件。resource 协议Web({ src: resource://rawfile/index.html })适用于运行时动态指定本地文件路径。loadData 渲染字符串通过controller.loadData(htmlStr, ...)直接渲染 HTML 字符串适用于加载富文本数据。import { webview } from kit.ArkWeb; import { BusinessError } from kit.BasicServicesKit; Entry Component struct WebLoadDemo { controller: webview.WebviewController new webview.WebviewController(); // 用于 loadData 渲染的 HTML 字符串 State htmlContent: string !DOCTYPE html htmlheadtitle动态内容/title/head body h1 stylecolor: #007AFF;这是动态生成的内容/h1 p无需任何文件直接渲染字符串/p /body/html; build() { Column() { Text(Web 组件四种加载方式演示) .fontSize(20) .fontWeight(FontWeight.Bold) .margin({ bottom: 10 }) Row({ space: 10 }) { // 方式一加载在线 URL Button(加载在线URL) .onClick(() { try { this.controller.loadUrl(https://developer.huawei.com/); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) // 方式二加载本地 rawfile Button(加载本地Rawfile) .onClick(() { try { this.controller.loadUrl($rawfile(index.html)); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) // 方式三使用 resource 协议 Button(Resource协议) .onClick(() { try { this.controller.loadUrl(resource://rawfile/index.html); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) // 方式四loadData 渲染字符串 Button(LoadData渲染) .onClick(() { try { this.controller.loadData( this.htmlContent, text/html, // mimeType UTF-8 // encoding ); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) } .margin({ bottom: 10 }) // Web 组件初始化默认加载本地页面 Web({ src: $rawfile(index.html), controller: this.controller }) .width(100%) .layoutWeight(1) .javaScriptAccess(true) .domStorageAccess(true) } .width(100%) .height(100%) .padding(20) } }代码要点解析加载在线 URL点击按钮后通过controller.loadUrl()加载华为开发者官网。注意需在module.json5中添加网络权限requestPermissions: [{ name: ohos.permission.INTERNET }]加载本地 rawfile使用$rawfile(index.html)静态引用指向src/main/resources/rawfile/index.html。该方式支持在 HTML 中正常引用同目录下的 CSS、JS、图片等资源。resource 协议使用resource://rawfile/index.html字符串格式与$rawfile()指向同一目录但它是纯字符串可以在运行时动态拼接和构造路径。loadData 渲染字符串无需任何文件直接将 HTML 内容作为字符串传入controller.loadData()进行渲染。适合做富文本邮件、协议弹窗、动态内容展示等场景。二、 ArkUI 与 Web 的双向通信机制双向通信是 Hybrid 开发的基石主要分为“正向调用”和“反向注入”两个方向1. 正向通信ArkUI 调用 Web (runJavaScript)原生端通过WebviewController的runJavaScript方法可以直接执行网页中的 JS 代码并支持通过回调获取返回值。this.controller.runJavaScript(webFunction(Hello from ArkUI!), (error, result) { if (!error) { console.info(Web返回数据 result); } });注意该方法必须在页面加载完成后调用如在onPageEnd回调中否则可能执行失败。2. 反向通信Web 调用 ArkUI (JavaScriptProxy)这是最核心的 JSBridge 实现方式。通过javaScriptProxy将 ArkUI 对象注入到 Webview 的window对象中前端即可像调用本地方法一样调用原生能力。ArkTS 侧代码// 1. 定义原生对象 class NativeObj { makePhoneCall(number: string): void { console.info(准备拨打电话 number); } } // 2. 在 Web 组件中注入 Web({ src: $rawfile(index.html), controller: this.controller }) .javaScriptProxy({ object: new NativeObj(), name: native, // 前端通过 window.native 访问 methodList: [makePhoneCall], // 允许调用的方法白名单 controller: this.controller })Web (JS) 侧调用window.native.makePhoneCall(10086);三、 动态注册与注销除了初始化时静态注入ArkUI 还支持在运行时动态管理注入对象动态注册使用controller.registerJavaScriptProxy(obj, name, methods)。刷新生效动态注册后必须执行controller.refresh()刷新页面注入才会生效注销对象使用controller.deleteJavaScriptRegister(name)移除已注入的对象。ArkTS 侧代码import { webview } from kit.ArkWeb; import { BusinessError } from kit.BasicServicesKit; // 1. 定义需要注入到前端的原生对象 class NativeBridge { sayHello(): string { return Hello from ArkTS Dynamic Proxy!; } } Entry Component struct DynamicProxyDemo { controller: webview.WebviewController new webview.WebviewController(); State bridgeObj: NativeBridge new NativeBridge(); build() { Column() { Row({ space: 10 }) { // 2. 动态注册按钮 Button(动态注册) .onClick(() { try { this.controller.registerJavaScriptProxy( this.bridgeObj, nativeBridge, // 前端访问的对象名 [sayHello] // 允许调用的方法白名单 ); // ⚠️ 核心步骤动态注册后必须刷新页面才能生效 this.controller.refresh(); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) // 3. 注销对象按钮 Button(注销对象) .onClick(() { try { this.controller.deleteJavaScriptRegister(nativeBridge); } catch (error) { console.error(ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}); } }) } .margin({ bottom: 10 }) // 4. Web 组件 Web({ src: $rawfile(index.html), controller: this.controller }) .width(100%) .layoutWeight(1) .javaScriptAccess(true) } .width(100%) .height(100%) .padding(20) } }Web (JS) 侧代码 (index.html)!DOCTYPE html html head titleDynamic Proxy Test/title /head body button onclickcallNative()调用原生方法/button p idresult/p script function callNative() { // 检查对象是否已注册 if (window.nativeBridge) { const msg window.nativeBridge.sayHello(); document.getElementById(result).innerText msg; } else { document.getElementById(result).innerText 原生对象未注册; } } /script /body /html代码要点解析动态注册时机与初始化时使用的.javaScriptProxy()不同registerJavaScriptProxy可以在任意运行时阶段调用例如在onPageEnd回调中或根据用户登录状态动态注入。强制刷新动态注入的对象不会自动同步到当前已加载的页面上下文中必须紧接着调用controller.refresh()重新加载页面注入才会真正生效。内存管理与注销当 H5 页面不再需要调用原生能力或者组件即将销毁时务必调用deleteJavaScriptRegister移除注入的对象。这能有效断开 ArkTS 对象与 WebView 之间的引用链防止内存泄漏。四、 进阶JSBridge 分层架构设计在实际的企业级 Hybrid 应用中建议不要将业务逻辑直接写在JavaScriptProxy中而是采用分层设计以提高通用性和灵活性通信层负责 ArkTS 与 JS 之间的数据传递屏蔽底层通信机制。通常将数据序列化为 JSON 字符串进行传递。通道层 (Channel)允许注册多种方法通道。JS 侧负责将 API 信息打包ArkTS 侧负责解包并分发给具体的处理方法处理完毕后再通过runJavaScript执行 JS 侧的回调函数。方法层具体的业务 API 实现如获取设备信息、拉起支付、路由跳转等。这种架构类似于小程序的底层通信规范能够极大地降低原生端与前端代码的耦合度便于后续的业务扩展与维护。1. ArkTS 侧通道层与通信层实现import web_webview from ohos.web.webview; // 1. 方法层定义具体的业务 API class DeviceService { getInfo(): string { return JSON.stringify({ model: HarmonyOS Device, osVersion: 5.0 }); } } // 2. 通道层负责解包、路由分发与回调 class ChannelManager { private deviceService: DeviceService new DeviceService(); // 处理来自 H5 的调用 call(channelType: string, objectJson: string): string { const params JSON.parse(objectJson); let result: any {}; if (channelType device.getInfo) { result this.deviceService.getInfo(); } else { result { code: -1, message: API not found }; } return JSON.stringify(result); } } // 3. 通信层注入到 Web 中的代理对象 class BridgeProxy { private channelManager: ChannelManager new ChannelManager(); // 这是唯一暴露给 JS 的通信方法 nativeMethod(channelType: string, objectJson: string): string { return this.channelManager.call(channelType, objectJson); } } Entry Component struct HybridPage { private webController: web_webview.WebviewController new web_webview.WebviewController(); private bridgeProxy: BridgeProxy new BridgeProxy(); build() { Column() { Web({ src: $rawfile(index.html), controller: this.webController }) .javaScriptProxy({ object: this.bridgeProxy, name: JSBridge, // 注入到 window 的对象名 methodList: [nativeMethod], // 通信白名单仅暴露通信层方法 controller: this.webController }) .width(100%) .height(100%) } } }2. Web (JS) 侧通道层与通信层实现!DOCTYPE html html headtitleJSBridge Layered Demo/title/head body button onclickcallNativeApi()获取设备信息/button p idresult/p script // 通道层封装打包与解包逻辑 function nativeCall(channelType, params) { const objectJson JSON.stringify(params); // 调用通信层注入的方法 const resultJson window.JSBridge window.JSBridge.nativeMethod(channelType, objectJson); return resultJson ? JSON.parse(resultJson) : null; } // 方法层业务调用 function callNativeApi() { const res nativeCall(device.getInfo, {}); document.getElementById(result).innerText JSON.stringify(res); } /script /body /html五、 安全权限管控Permission 配置在真实业务中将原生能力暴露给 H5 存在极大的安全风险。HarmonyOS 提供了细粒度的权限控制机制允许在javaScriptProxy注入时通过 JSON 字符串限制访问来源。核心逻辑权限配置分为对象级和方法级。对象级权限指定哪些 URL 可以访问该对象的所有方法方法级权限则进一步细化定义特定 URL 对特定方法的访问权。.javaScriptProxy({ object: this.nativeObj, name: native, methodList: [makePhoneCall, getUserInfo], controller: this.controller, // 安全权限配置 permission: { javascriptProxyPermission: { urlPermissionList: [ { scheme: https, host: trusted-domain.com, port: , path: } ], methodList: [ { methodName: makePhoneCall, urlPermissionList: [ { scheme: https, host: trusted-domain.com, port: , path: }, { scheme: resource, host: rawfile, port: , path: } ] } ] } } })安全建议务必严格校验event.origin或来源 URL避免动态拼接 JS 字符串防止 XSS 注入攻击。六、 高级通信通道WebMessagePort对于高频交互、大数据量传输或复杂的实时业务如 IM 聊天、实时音视频状态同步传统的runJavaScript和JavaScriptProxy可能面临性能瓶颈。此时推荐使用基于 HTML5 标准的WebMessagePort机制。核心逻辑原生侧调用createWebMessagePorts()创建一对消息端口[port1, port2]。将port2通过postMessage发送给 H5H5 接收后缓存该端口并监听onmessage事件。双方后续均通过port.postMessage()进行双向异步通信无需反复注入对象或执行 JS 脚本。// ArkTS 侧创建并分发端口 Button(创建数据通道) .onClick(() { const ports this.controller.createWebMessagePorts(); // 将 port[1] 传递给 H5 this.controller.postMessage(init_port, [ports[1]]); // 监听来自 H5 的消息 ports[0].onMessageEvent (message: webview.WebMessage) { console.info(收到H5消息: message.data); }; })七、 渲染性能优化同层渲染与组件鸿蒙化在 Hybrid 应用中如果 H5 内嵌了大量的原生交互组件如视频播放器、地图、复杂表单传统的 WebView 渲染会导致严重的性能损耗和交互割裂感。核心优化策略同层渲染利用 ArkWeb 提供的同层渲染能力将部分 H5 节点替换为原生 ArkUI 组件。原生组件直接嵌入到 WebView 的渲染树中获得与原生应用一致的滑动跟手体验和渲染效率。API 鸿蒙化针对 H5 侧强依赖的平台相关 API提供一套完整的 HarmonyOS 版本实现参考成熟的小程序框架规范确保原有前端业务逻辑无需大幅改动即可无缝运行在鸿蒙环境中。生命周期管理确保所有的 JSBridge 通信都在 H5 页面加载完成onPageEnd之后发起避免调用未定义的函数或对象导致异常。同时在页面销毁时及时注销代理对象防止内存泄漏。八、 架构分层MVVM 模式下的 Web 容器设计在大型应用中直接在 UI 组件中处理复杂的 Web 交互逻辑会导致代码臃肿且难以维护。建议引入 MVVM 模式进行分层解耦View 层UI 组件Web组件仅负责渲染和展示不包含任何业务逻辑。它通过Link或Prop接收来自 ViewModel 的状态如 URL、加载进度并通过事件回调通知 ViewModel 用户的交互如页面开始加载、加载完成。ViewModel 层状态与逻辑作为 Web 容器的核心管理者ViewModel 持有WebviewController实例并管理页面状态如加载进度、错误信息。它处理所有业务逻辑如权限校验、URL 拦截、错误重试等并更新状态以驱动 UI 变化。Model 层数据源定义 Web 容器的数据结构和配置可以来自本地配置或远程接口实现 Web 容器的动态化配置。九、 通信协议标准化的 JSBridge 设计直接使用runJavaScript和JavaScriptProxy进行通信会导致代码耦合度高难以维护。建议设计一套标准化的通信协议统一消息格式定义一套统一的 JSON 消息格式包含action操作类型、params参数、callbackId回调 ID等字段。所有 ArkTS 与 H5 的通信都遵循此格式。通道管理器Channel Manager在 ArkTS 侧实现一个通道管理器负责接收 H5 发来的消息根据action字段分发给不同的业务模块处理。处理完成后通过callbackId找到对应的回调函数并将结果返回给 H5。H5 SDK 封装在 H5 侧封装一个 JS SDK提供统一的invoke方法。H5 开发者只需调用invoke(actionName, params).then(...)SDK 内部负责消息的序列化、发送和回调管理对 ArkTS 的通信细节完全透明。十、 性能优化渲染与交互体验当 Web 页面内嵌大量原生交互组件时传统的 WebView 渲染会导致严重的性能损耗和交互割裂感。同层渲染Peer Rendering利用 ArkWeb 提供的同层渲染能力将部分 H5 节点如视频播放器、地图替换为原生 ArkUI 组件。原生组件直接嵌入到 WebView 的渲染树中获得与原生应用一致的滑动跟手体验和渲染效率。WebMessagePort 高频通信对于高频交互、大数据量传输或复杂的实时业务如 IM 聊天、实时音视频状态同步传统的runJavaScript和JavaScriptProxy可能面临性能瓶颈。此时推荐使用基于 HTML5 标准的WebMessagePort机制建立双向异步通信通道避免反复注入对象或执行 JS 脚本。预加载与缓存策略对于核心业务页面可以在应用启动时预加载 Web 容器并缓存静态资源。通过WebviewController的缓存接口配置合理的缓存策略减少网络请求提升页面加载速度。