1. 引言
在鸿蒙应用开发中,Web组件是一个强大的工具,它允许开发者在原生应用中嵌入和展示Web内容。但仅仅展示网页还不够,真正的价值在于原生应用与前端页面之间的双向通信能力。通过Web组件,鸿蒙NEXT应用可以调用前端页面的JavaScript函数,实现数据交换和功能互动,这为混合开发模式开辟了新的可能性。
本文将深入探讨如何在鸿蒙NEXT中使用Web组件与前端JavaScript进行交互,包括基础用法、高级技巧和实际应用场景。
2. Web组件基础
2.1 Web组件简介
Web组件是鸿蒙系统提供的用于显示网页内容的组件,它基于强大的Web渲染引擎,能够高效地加载和显示Web页面。基本使用方法如下:
typescript
import web_webview from '@ohos.web.webview'; @Entry @Component struct WebComponent { webviewController: web_webview.WebviewController = new web_webview.WebviewController(); build() { Column() { // 加载在线网页 Web({ src: 'https://www.example.com', controller: this.webviewController }) // 或加载本地网页 // Web({ src: $rawfile('index.html'), controller: this.webviewController }) } } }
2.2 常用属性配置
为了使Web组件更好地与JavaScript交互,需要配置一些重要属性:
typescript
Web({ src: $rawfile('index.html'), controller: this.controller }) .javaScriptAccess(true) // 启用JavaScript执行 .fileAccess(true) // 允许访问本地文件 .zoomAccess(true) // 允许缩放 .onPageEnd((e) => { // 页面加载完成回调 console.info('页面加载完成'); })
3. 应用侧调用前端JavaScript函数
3.1 runJavaScript方法
鸿蒙NEXT提供了runJavaScript()
方法,允许应用侧直接调用前端页面中的JavaScript函数。
前端页面代码:
html
<!DOCTYPE html> <html> <body> <script> // 无参函数 function htmlTest() { console.info('JavaScript Hello World! '); document.getElementById('demo').innerHTML = '内容已更新'; } // 有参函数 function updateContent(newContent) { document.getElementById('demo').innerHTML = newContent; } </script> <p id="demo">初始内容</p> </body> </html>
应用侧调用代码:
typescript
import web_webview from '@ohos.web.webview'; @Entry @Component struct WebComponent { webviewController: web_webview.WebviewController = new web_webview.WebviewController(); build() { Column() { Web({ src: $rawfile('index.html'), controller: this.webviewController}) Button('调用无参函数') .onClick(() => { // 调用无参函数 this.webviewController.runJavaScript('htmlTest()'); }) Button('调用有参函数') .onClick(() => { // 调用有参函数 this.webviewController.runJavaScript('updateContent("来自鸿蒙应用的内容")'); }) } } }
3.2 执行JavaScript代码块
除了调用现有函数,还可以直接执行JavaScript代码块:
typescript
Button('执行代码块') .onClick(() => { // 直接执行JavaScript代码 this.webviewController.runJavaScript( `document.body.style.backgroundColor = '#f0f0f0'; console.log('背景色已更改');` ); })
3.3 处理返回值
runJavaScript
方法可以接收前端函数的返回值:
typescript
Button('获取返回值') .onClick(() => { this.webviewController.runJavaScript('getData()', (error, result) => { if (error) { console.error(`执行失败: ${error}`); return; } console.info(`收到返回值: ${result}`); // 处理结果 }); })
4. 前端调用应用侧方法
4.1 注册应用侧对象
为了使前端页面能够调用应用侧的方法,需要将应用侧的对象注册到Web组件中。
方式一:初始化时注册(javaScriptProxy)
typescript
import web_webview from '@ohos.web.webview'; // 定义要注册的类 class AppSideObject { test(): string { return 'ArkTS Hello World!'; } showMessage(message: string): string { console.info(`收到前端消息: ${message}`); return `已收到: ${message}`; } } @Entry @Component struct WebComponent { webviewController: web_webview.WebviewController = new web_webview.WebviewController(); @State appObj: AppSideObject = new AppSideObject(); build() { Column() { Web({ src: $rawfile('index.html'), controller: this.webviewController}) .javaScriptProxy({ object: this.appObj, name: "harmonyApp", // 前端通过此名称访问 methodList: ["test", "showMessage"], // 暴露的方法列表 controller: this.webviewController }) } } }
方式二:初始化后注册(registerJavaScriptProxy)
typescript
import web_webview from '@ohos.web.webview'; import business_error from '@ohos.base'; @Entry @Component struct WebComponent { webviewController: web_webview.WebviewController = new web_webview.WebviewController(); @State appObj: AppSideObject = new AppSideObject(); build() { Column() { Button('注册对象到前端') .onClick(() => { try { this.webviewController.registerJavaScriptProxy( this.appObj, "harmonyApp", ["test", "showMessage"] ); // 注册后需要刷新才能生效 this.webviewController.refresh(); } catch (error) { let e: business_error.BusinessError = error as business_error.BusinessError; console.error(`ErrorCode: ${e.code}, Message: ${e.message}`); } }) Web({ src: $rawfile('index.html'), controller: this.webviewController }) } } }
4.2 前端调用方法
注册完成后,前端页面可以通过注册的对象名称调用应用侧的方法:
html
<!DOCTYPE html> <html> <body> <button type="button" οnclick="callHarmony()">调用鸿蒙方法</button> <p id="result"></p> <script> function callHarmony() { // 调用应用侧方法 let result = harmonyApp.showMessage('你好,鸿蒙!'); document.getElementById("result").innerHTML = result; console.info('调用成功: ' + result); } </script> </body> </html>
4.3 复杂数据类型传递
应用侧和前端之间可以传递复杂数据类型,如数组和对象:
typescript
class DataHandler { getComplexData(): Array<Number> { return [1, 2, 3, 4]; } processUserData(user: Object): string { console.info(`收到用户数据: ${JSON.stringify(user)}`); return "处理成功"; } }
前端调用:
javascript
// 获取数组数据 let dataArray = harmonyApp.getComplexData(); console.log(dataArray); // 传递对象数据 let user = {name: '张三', age: 25}; let result = harmonyApp.processUserData(user);
5. 建立双向数据通道
除了函数调用,鸿蒙NEXT还提供了更强大的Web消息端口(WebMessagePort)机制,用于建立应用侧和前端页面之间的双向数据通道。
5.1 创建消息端口
typescript
// xxx.ets import web_webview from '@ohos.web.webview'; @Entry @Component struct WebComponent { controller: web_webview.WebviewController = new web_webview.WebviewController(); ports: web_webview.WebMessagePort[]; @State receivedFromHtml: string = '等待接收前端消息'; build() { Column() { Text(this.receivedFromHtml) .fontSize(20) .margin(10) Button('建立数据通道') .onClick(() => { // 1. 创建两个消息端口 this.ports = this.controller.createWebMessagePorts(); // 2. 将一个端口发送到前端页面 this.controller.postMessage("__init_port__", [this.ports[0]], "*"); // 3. 在另一个端口上监听消息 this.ports[1].onMessageEvent((result: web_webview.WebMessage) => { if (typeof result.getData() === 'string') { this.receivedFromHtml = result.getData(); } }); }) Web({ src: $rawfile('index.html'), controller: this.controller }) } } }
5.2 前端页面处理消息端口
html
<!-- index.html --> <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView Message Port Demo</title> </head> <body> <h1>WebView Message Port Demo</h1> <input id="messageInput" type="text" placeholder="输入要发送的消息"> <button οnclick="sendMessage()">发送消息到应用侧</button> <p id="receivedMessage">等待接收应用侧消息...</p> <script> let remotePort = null; // 监听应用侧发送的消息端口 window.addEventListener('message', function(event) { if (event.data === '__init_port__') { remotePort = event.ports[0]; // 监听来自应用侧的消息 remotePort.onmessage = function(event) { document.getElementById('receivedMessage').innerHTML = event.data; }; } }); function sendMessage() { if (!remotePort) { console.error('消息端口未初始化'); return; } const message = document.getElementById('messageInput').value; remotePort.postMessage(message); } </script> </body> </html>
6. 实际应用场景
6.1 身份验证令牌传递
一种常见场景是在原生应用中处理用户认证,然后将令牌传递给前端页面:
typescript
class AuthHandler { getAuthToken(): string { // 从应用侧获取认证令牌 return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; } }
前端页面使用:
javascript
// 前端页面获取认证令牌 const token = harmonyApp.getAuthToken(); // 使用令牌访问受保护的API fetch('/api/protected-data', { headers: { 'Authorization': `Bearer ${token}` } });
6.2 原生功能调用
前端页面可以调用设备原生功能,如相机、GPS等:
typescript
class DeviceService { takePicture(): string { // 调用原生相机功能 // 返回图片路径或Base64编码 return "file://path/to/image.jpg"; } getLocation(): Object { // 获取设备位置 return {latitude: 39.9042, longitude: 116.4074}; } }
6.3 数据持久化
前端页面可以利用应用侧的数据持久化能力:
typescript
class StorageService { saveData(key: string, value: string): void { // 使用原生存储保存数据 Preferences.putSync(key, value); } loadData(key: string): string { // 从原生存储读取数据 return Preferences.getSync(key, ''); } }
7. 调试与错误处理
7.1 启用调试模式
在开发过程中,可以启用Web调试功能:
typescript
aboutToAppear() { // 配置Web开启调试模式 webview.WebviewController.setWebDebuggingAccess(true); }
然后可以在Chrome浏览器中通过chrome://inspect
检查Web页面。
7.2 错误处理
在JavaScript交互中添加适当的错误处理:
typescript
// 调用JavaScript时的错误处理 this.webviewController.runJavaScript('someFunction()', (error, result) => { if (error) { console.error(`JavaScript执行错误: ${error.code}, ${error.message}`); // 处理错误 return; } // 处理成功结果 }); // 注册对象时的错误处理 try { this.webviewController.registerJavaScriptProxy(this.appObj, "harmonyApp", ["method1"]); this.webviewController.refresh(); } catch (error) { let e: business_error.BusinessError = error as business_error.BusinessError; console.error(`注册失败: ErrorCode: ${e.code}, Message: ${e.message}`); }
8. 总结
鸿蒙NEXT的Web组件提供了强大的原生应用与前端JavaScript交互能力,通过本文介绍的几种方式,开发者可以实现:
应用侧调用前端函数:使用
runJavaScript()
方法直接调用前端页面中的JavaScript函数前端调用应用侧功能:通过
javaScriptProxy()
或registerJavaScriptProxy()
注册应用侧对象供前端调用建立双向通信通道:使用Web消息端口实现应用侧和前端页面的持续双向通信
这些功能为开发丰富的混合应用提供了坚实基础,结合了原生应用的性能优势和Web开发的灵活性。在实际项目中,开发者可以根据具体需求选择合适的交互方式,打造无缝的用户体验。