删除线格式 ## 【HarmonyOS 5】鸿蒙 Web 组件和内嵌网页双向通信 DEMO 示例
一、前言
在 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@Componentstruct 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@Componentstruct 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);}
   复制代码
 
评论