【HarmonyOS 5】鸿蒙Web组件和内嵌网页双向通信DEMO示例

简介: 【HarmonyOS 5】鸿蒙Web组件和内嵌网页双向通信DEMO示例


##鸿蒙开发能力 ##HarmonyOS SDK应用服务##鸿蒙金融类应用 (金融理财#

一、前言

在 ArkUI 开发中,Web 组件(Web)允许开发者在应用内嵌入网页,实现混合开发场景。

本文将通过完整 DEMO,详解如何通过WebviewController实现 ArkUI 与内嵌网页的双向通信,涵盖 ArkUI 调用网页 JS、网页调用 ArkUI 对象的核心技术点。

二、双向通信实现原理

1、双向通信概念
Web 到 ArkUI(反向通信)通过registerJavaScriptProxy将 ArkUI 对象注册到网页的window对象,允许网页通过window.xxx调用 ArkUI 暴露的方法。

ArkUI 到 Web(正向通信)通过runJavaScript执行网页 JS 代码,支持回调获取返回值,实现原生代码调用网页函数。

2、双向通信流程图
ArkUI Web
┌──────────────┐ ┌──────────────┐
│ registerJS ├───────────── window.objName │
│ (反向注册) │ ├──────────────┤
├──────────────┤ │ call test() │
│ runJavaScript├───────────── execute JS code │
│ (正向调用) │ ├──────────────┤
└──────────────┘ └──────────────┘


三、双向通信实现步骤

1、ArkUI 定义可被网页调用的对象
创建一个TestObj类,声明允许网页调用的方法(白名单机制):

class TestObj {
  // 网页可调用的方法1:返回字符串
  test(): string {
    return "ArkUI Web Component";
  }
  // 网页可调用的方法2:打印日志
  toString(): void {
    console.log('Web Component toString');
  }
  // 网页可调用的方法3:接收网页消息
  receiveMessageFromWeb(message: string): void {
    console.log(`Received from web: ${message}`);
  }
}

2、ArkUI 组件核心代码
初始化控制器与状态

@Entry
@Component
struct WebComponent {
  // Webview控制器
  controller: webview.WebviewController = new webview.WebviewController();
  // 注册到网页的ArkUI对象
  @State testObj: TestObj = new TestObj();
  // 注册名称(网页通过window.[name]访问)
  @State regName: string = 'objName';
  // 接收网页返回数据
  @State webResult: string = '';
  build() { /* 组件布局与交互逻辑 */ }
}

布局与交互按钮,添加三个核心功能按钮:

Column() {
  // 显示网页返回数据
  Text(`Web返回数据:${this.webResult}`).fontSize(16).margin(10);
  // 1. 注册ArkUI对象到网页
  Button('注册到Window')
    .onClick(() => {
      this.controller.registerJavaScriptProxy(
        this.testObj,  // ArkUI对象
        this.regName,  // 网页访问名称
        ["test", "toString", "receiveMessageFromWeb"]  // 允许调用的方法白名单
      );
    })
  // 2. ArkUI调用网页JS
  Button('调用网页函数')
    .onClick(() => {
      this.controller.runJavaScript(
        'webFunction("Hello from ArkUI!")',  // 执行网页JS代码
        (error, result) => {  // 回调处理返回值
          if (!error) this.webResult = result || '无返回值';
        }
      );
    })
  // 3. Web组件加载
  Web({ src: $rawfile('index.html'), controller: this.controller })
    .javaScriptAccess(true)  // 开启JS交互权限
    .onPageEnd(() => {  // 页面加载完成时触发
      // 页面加载后自动调用网页测试函数
      this.controller.runJavaScript('initWebData()');
    })
}

3. registerJavaScriptProxy的实际作用是,将 ArkUI 对象绑定到网页window,实现反向通信。

registerJavaScriptProxy(
  obj: Object,        // ArkUI中定义的对象
  name: string,       // 网页访问的名称(如window.name)
  methods: string[]   // 允许调用的方法白名单(严格匹配方法名)
);

源码示例

完整代码已上传至Gitee 仓库,欢迎下载调试!如果有任何问题,欢迎在评论区留言交流~

项目结构
├── xxx.ets # ArkUI组件代码
└── index.html # 内嵌网页文件

WebViewPage.ets

import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class TestObj {
  constructor() {
  }
  test(): string {
    return "ArkUI Web Component";
  }
  toString(): void {
    console.log('Web Component toString');
  }
  receiveMessageFromWeb(message: string): void {
    console.log(`Received message from web: ${message}`);
  }
}
@Entry
@Component
struct WebViewPage {
  controller: webview.WebviewController = new webview.WebviewController();
  @State testObjtest: TestObj = new TestObj();
  @State name: string = 'objName';
  @State webResult: string = '';
  build() {
    Column() {
      Text(this.webResult).fontSize(20)
      Button('refresh')
        .onClick(() => {
          try {
            this.controller.refresh();
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
      Button('Register JavaScript To Window')
        .onClick(() => {
          try {
            this.controller.registerJavaScriptProxy(this.testObjtest, this.name, ["test", "toString", "receiveMessageFromWeb"]);
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
      Button('deleteJavaScriptRegister')
        .onClick(() => {
          try {
            this.controller.deleteJavaScriptRegister(this.name);
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
      Button('Send message to web')
        .onClick(() => {
          try {
            this.controller.runJavaScript(
              'receiveMessageFromArkUI("Hello from ArkUI!")',
              (error, result) => {
                if (error) {
                  console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
                  return;
                }
                console.info(`Message sent to web result: ${result}`);
              }
            );
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
      Button('Get data from web')
        .onClick(() => {
          try {
            this.controller.runJavaScript(
              'getWebPageData()',
              (error, result) => {
                if (error) {
                  console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
                  return;
                }
                if (result) {
                  this.webResult = result;
                  console.info(`Data from web: ${result}`);
                }
              }
            );
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
      Web({ src: $rawfile('index.html'), controller: this.controller })
        .javaScriptAccess(true)
        .onPageEnd(e => {
          try {
            this.controller.runJavaScript(
              'test()',
              (error, result) => {
                if (error) {
                  console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
                  return;
                }
                if (result) {
                  this.webResult = result;
                  console.info(`The test() return value is: ${result}`);
                }
              }
            );
            if (e) {
              console.info('url: ', e.url);
            }
          } catch (error) {
            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          }
        })
        .width("100%")
        .height("50%")
    }
    .width("100%")
    .height("100%")
    .backgroundColor(Color.Black)
  }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Communication</title>
</head>
<body>
<button onclick="callArkUIMethod()">Call ArkUI Method</button>
<button onclick="sendMessageToArkUI()">Send message to ArkUI</button>
<div id="messageFromArkUI"></div>
<script>
    function callArkUIMethod() {
        const result = window.objName.test();
        console.log("Result from ArkUI: ", result);
    }
    function sendMessageToArkUI() {
        window.objName.receiveMessageFromWeb('Hello from web!');
    }
    function receiveMessageFromArkUI(message) {
        const messageDiv = document.getElementById('messageFromArkUI');
        messageDiv.textContent = `Received message from ArkUI: ${message}`;
    }
    function getWebPageData() {
        return "Data from web page";
    }
    function test() {
        return "Test function result from web";
    }
</script>
</body>
</html>

注意

组件销毁时调用deleteJavaScriptRegister(name)取消注册,避免内存泄漏:

onDestroy() {
  this.controller.deleteJavaScriptRegister(this.regName);
}
目录
相关文章
|
13天前
|
开发者 容器
鸿蒙应用开发从入门到实战(十四):ArkUI组件Column&Row&线性布局
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解Column和Row组件的使用以及线性布局的方法。
108 12
|
14天前
|
API 数据处理
鸿蒙应用开发从入门到实战(十三):ArkUI组件Slider&Progress
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解滑块Slider和进度条Progress组件的使用。
|
4月前
|
开发者
鸿蒙仓颉开发语言实战教程:自定义组件
本文介绍了如何在仓颉开发语言中创建自定义组件,以封装和管理项目中的 tabbar 组件为例。通过创建独立的组件文件 yltabbar.cj,并使用 @Component 和 @Link 等修饰符实现组件化开发与参数传递,提升代码复用性和项目可维护性。适合希望深入掌握仓颉语言组件开发的 HarmonyOS 开发者学习参考。 #HarmonyOS #仓颉 #购物
|
16天前
|
数据安全/隐私保护 开发者
鸿蒙应用开发从入门到实战(十一):ArkUI组件Text&TextInput
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解文本组件Text和TextInput的使用。
110 3
|
缓存 数据安全/隐私保护 JavaScript
【HarmonyOS 5】鸿蒙页面和组件生命周期函数
【HarmonyOS 5】鸿蒙页面和组件生命周期函数
176 0
|
4月前
|
存储 IDE 定位技术
【HarmonyOS 5】鸿蒙组件&模板服务详解 - 助力高效开发的利器
在移动应用开发领域,效率与质量始终是开发者追求的核心目标。鸿蒙系统作为新兴的操作系统,为开发者提供了丰富且强大的开发资源,其中鸿蒙组件&模板服务更是成为开发者快速构建高质量应用的得力助手。
138 0
鸿蒙仓颉语言开发教程:页面和组件的生命周期
仓颉语言中的生命周期指页面或组件从加载到消失的过程。与ArkTs不同,仓颉需在生命周期方法前添加`protected open`修饰符,如`aboutToAppear()`、`onPageShow()`等。部分函数如`onBackPress()`返回布尔值,决定是否拦截系统返回操作。仅`@Entry`组件支持全部生命周期,普通组件仅支持`aboutToAppear`和`aboutToDisappear`。掌握正确写法可避免踩坑。
|
4月前
|
缓存
鸿蒙5开发宝藏案例分享---Swiper组件性能优化实战
本文分享了鸿蒙系统中Swiper组件的性能优化技巧,包括:1) 使用`LazyForEach`替代`ForEach`实现懒加载,显著降低内存占用;2) 通过`cachedCount`精准控制缓存数量,平衡流畅度与内存消耗;3) 利用`onAnimationStart`在抛滑时提前加载资源,提升构建效率;4) 添加`@Reusable`装饰器复用组件实例,减少创建开销。实际应用后,图库页帧率从45fps提升至58fps,效果显著。适合处理复杂列表或轮播场景,欢迎交流经验!
|
14天前
|
API 数据处理
鸿蒙应用开发从入门到实战(十三):ArkUI组件Slider&Progress
ArkUI提供滑块Slider与进度条Progress组件,用于鸿蒙原生APP开发。Slider支持拖动调节音量、亮度等,可设步长、方向及提示气泡;Progress支持线性、环形等多种样式,可自定义颜色、宽度与刻度,实时显示任务进度。
110 2
|
15天前
|
开发者
鸿蒙应用开发从入门到实战(十二):ArkUI组件Button&Toggle
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解按钮组件Button和Toggle的使用。
110 2