HarmonyOS鸿蒙Next中往web传json map问题

HarmonyOS鸿蒙Next中往web传json map问题 网页内嵌了一个web组件,需要点击ArkUI按钮将json数据传输到webview内,应该如何实现呢?

10 回复
  1. ArkTS 侧实现 (发送方)

**核心思路:**将 JSON 对象序列化为字符串,通过 runJavaScript 调用网页中预定义的全局函数。

import { webview } from '@kit.ArkWeb';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct WebDataTransferPage {
  // 1. 创建控制器实例
  private controller: webview.WebviewController = new webview.WebviewController();
  
  // 模拟要发送的 JSON 数据
  @State jsonData: object = {
    userId: "10086",
    userName: "VoyahUser",
    timestamp: Date.now()
  };

  build() {
    Column() {
      // 2. ArkUI 按钮
      Button('发送 JSON 到 WebView')
        .width('80%')
        .height(50)
        .margin(20)
        .onClick(() => {
          this.sendDataToWeb();
        })

      // 3. Web 组件
      Web({ src: $rawfile('index.html'), controller: this.controller })
        .javaScriptAccess(true) // 【关键】必须开启 JavaScript 支持
        .onPageEnd(() => {
          console.info('页面加载完成,可以开始通信');
        })
        .onConsole((event) => {
          // 监听网页打印的日志,方便调试
          console.info(`[Web Console]: ${event.message.getMessage()}`);
          return true;
        })
    }
    .width('100%')
    .height('100%')
  }

  /**
   * 核心方法:发送数据
   */
  private sendDataToWeb() {
    // 1. 将 JSON 对象转换为字符串
    const jsonString = JSON.stringify(this.jsonData);
    
    // 2. 构造要在网页中执行的 JS 代码
    // 假设网页中有一个名为 receiveDataFromArk 的全局函数
    const jsCode = `if (typeof receiveDataFromArk === 'function') { 
      receiveDataFromArk('${jsonString}'); 
    } else { 
      console.log('网页未定义 receiveDataFromArk 函数'); 
    }`;

    // 3. 执行 JS
    try {
      this.controller.runJavaScript(jsCode).then(() => {
        console.info('JSON 数据发送成功');
      }).catch((err: Error) => {
        console.error('发送失败:', err.message);
      });
    } catch (error) {
      console.error('runJavaScript 异常:', error);
    }
  }
}
  1. Web 侧实现 (接收方)

在你的 $rawfile/index.html 或远程网页中,需要定义一个全局函数来接收并解析数据。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HarmonyOS Web Demo</title>
</head>
<body>
    <h1>等待 ArkUI 发送数据...</h1>
    <div id="result" style="padding: 20px; background: #f0f0f0;">暂无数据</div>

    <script>
        /**
         * 定义全局函数供 ArkTS 调用
         * @param {string} jsonStr - ArkTS 传来的 JSON 字符串
         */
        function receiveDataFromArk(jsonStr) {
            console.log("收到 ArkTS 数据:", jsonStr);
            
            try {
                // 1. 解析 JSON 字符串
                const data = JSON.parse(jsonStr);
                
                // 2. 更新页面 UI
                document.getElementById('result').innerHTML = 
                    `用户ID: ${data.userId}<br>用户名: ${data.userName}`;
                
                // 3. 可以在这里执行其他业务逻辑
                alert("数据接收成功!");
                
            } catch (e) {
                console.error("JSON 解析失败:", e);
            }
        }
    </script>
</body>
</html>

总结建议

  • 简单推送:使用 runJavaScript 调用网页全局函数,这是最直接的方式。
  • 安全性:务必在 Web 组件中开启 .javaScriptAccess(true)。
  • 调试:利用 .onConsole() 回调,你可以在 DevEco Studio 的 HiLog 中直接看到网页 console.log 的输出,这对排查 JSON 格式问题非常有帮助。

更多关于HarmonyOS鸿蒙Next中往web传json map问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


APP应用侧可以通过runJavaScript()runJavaScriptExt()方法调用前端页面的JavaScript相关函数。

runJavaScript()runJavaScriptExt()在参数类型上有以下差异:runJavaScriptExt()支持string和ArrayBuffer类型参数,而runJavaScript()仅支持string类型参数。

在下面的示例中,点击应用侧的“runJavaScript”按钮时,触发前端页面的htmlTest()方法。

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<h1 id="text">这是一个测试信息,默认字体为黑色,调用runJavaScript方法后字体为黄色、调用runJavaScriptParam方法后字体为绿色、调用runJavaScriptCodePassed方法后字体为红色</h1>
<script>
    // 有参函数。
    var param = "param: JavaScript Hello World!";
    function htmlTestParam(param) {
        document.getElementById('text').style.color = 'green';
        console.info(param);
    }
    // 无参函数。
    function htmlTest() {
        document.getElementById('text').style.color = 'yellow';
    }
    // 点击“Click Me!”按钮,触发前端页面callArkTS()函数执行JavaScript传递的代码。
    function callArkTS() {
        changeColor();
    }
</script>
</body>
</html>
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
  webviewController: webview.WebviewController = new webview.WebviewController();
  aboutToAppear() {
    // 配置Web开启调试模式
    webview.WebviewController.setWebDebuggingAccess(true);
  }
  build() {
    Column() {
      Button('runJavaScriptParam')
        .onClick(() => {
          // 调用前端页面有参函数。
          this.webviewController.runJavaScript('htmlTestParam(param)');
        })
      Button('runJavaScript')
        .onClick(() => {
          // 调用前端页面无参函数。
          this.webviewController.runJavaScript('htmlTest()');
        })
      Button('runJavaScriptCodePassed')
        .onClick(() => {
          // 传递runJavaScript侧代码方法。
          this.webviewController.runJavaScript(
            `function changeColor(){document.getElementById('text').style.color = 'red'}`);
        })
      Web({ src: $rawfile('index.html'), controller: this.webviewController })
    }
  }
}

核心原理

无论使用哪种框架,核心逻辑都是:

  1. 原生侧(ArkUI): 拦截按钮点击事件,调用 Web 组件提供的 JavaScript 注入接口。
  2. 数据转换: 将原生侧的 Map/Object 转换为 JSON 字符串。
  3. Web 侧(JS): 编写一个全局函数来接收这个字符串,并解析回 JSON 对象使用。

方案一:HarmonyOS ArkUI (ArkTS)

如果你是在开发鸿蒙应用,使用的是 @ohos.web.webview 组件。

ArkUI 侧 (ArkTS)

你需要使用 runJavaScript 方法来执行一段 JS 代码,将数据传进去。

import web_webview from '@ohos.web.webview';

@Entry
@Component
struct WebPage {
  // 创建一个 WebController 来控制 Web 组件
  controller: web_webview.WebviewController = new web_webview.WebviewController();
  // 模拟要传输的 JSON Map
  private myData: Record<string, any> = { "name": "张三", "age": 18, "city": "Shanghai" };

  build() {
    Column() {
      // 按钮
      Button('发送数据到Web')
        .onClick(() => {
          // 1. 将 Map 转换为 JSON 字符串
          let jsonStr = JSON.stringify(this.myData);
          
          // 2. 构造要执行的 JS 代码
          // 假设 Web 页面里有一个叫 receiveDataFromNative 的函数
          let jsCommand = `receiveDataFromNative('${jsonStr}')`;

          // 3. 调用 runJavaScript 注入代码
          this.controller.runJavaScript(jsCommand);
        })

      // Web 组件
      Web({ src: 'www.example.com/index.html', controller: this.controller })
    }
  }
}

Web 侧 (HTML/JS)

在网页中定义对应的接收函数:

// 定义全局函数供原生调用
function receiveDataFromNative(jsonStr) {
    console.log("收到原生数据:", jsonStr);
    try {
        var data = JSON.parse(jsonStr);
        // 更新 UI 或处理逻辑
        document.getElementById("content").innerText = "姓名: " + data.name;
    } catch (e) {
        console.error("解析 JSON 失败", e);
    }
}

方案二:Flutter (Webview)

如果你使用的是 Flutter 的 webview_flutter 插件。

Flutter 侧 (Dart)

使用 runJavascript 方法。

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class MyWebView extends StatefulWidget {
  @override
  _MyWebViewState createState() => _MyWebViewState();
}

class _MyWebViewState extends State<MyWebView> {
  late final WebViewController _controller;
  // 模拟 JSON Map
  final Map<String, dynamic> myData = {"name": "李四", "score": 95};

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: Icon(Icons.send),
            onPressed: () async {
              // 1. 转换为 JSON 字符串
              String jsonStr = jsonEncode(myData);
              
              // 2. 转义单引号(防止 JSON 里的双引号破坏 JS 语法,虽然通常 encode 处理好了,但稳妥起见)
              // 注意:这里最好用单引号包裹参数,因为 JSON 内部全是双引号
              String js = "receiveDataFromNative($jsonStr)"; 
              
              // 3. 执行 JS
              _controller.runJavaScript(js);
            },
          )
        ],
      ),
      body: WebView(
        initialUrl: 'assets/index.html',
        javascriptMode: JavascriptMode.unrestricted, // 必须开启 JS
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
        },
      ),
    );
  }
}

Web 侧

同上,定义 receiveDataFromNative 函数。


方案三:Android 原生 (WebView)

如果你是在写 Android 原生 ArkUI 组件或 Activity。

Android 侧 (Kotlin/Java)

使用 evaluateJavascript

val data = mapOf("id" to 101, "status" to "active")
val jsonStr = JSONObject(data).toString()

// 注意转义,Kotlin 中可以使用 raw string """...""" 来简化
val jsCode = "javascript:receiveDataFromNative('$jsonStr')"

webView.evaluateJavascript(jsCode, null)

关键注意事项

  1. JS 注入时机:

    • 确保 Web 页面已经加载完成(例如监听了 onPageFinishedWebLifecycle 的 Ready 状态)之后再发送数据,否则 JS 函数还没定义,调用会失败。
    • 如果按钮点击时页面还没好,建议先把数据缓存起来,等页面加载完再发。
  2. 字符转义(非常重要):

    • JSON 字符串通常包含双引号 "
    • 如果 JS 函数调用写成 func(" + jsonStr + "),容易因为 JSON 里的双引号导致 JS 语法错误。
    • 推荐做法: 在拼接 JS 字符串时,尽量利用 JSON 本身是双引号的特性,或者做好转义。
    • 最佳实践: 很多框架(如 Flutter)的 runJavaScript 其实可以直接接受对象(取决于版本),或者你需要手动确保生成的字符串是合法的 JS 代码。
  3. 安全性:

    • 不要传输敏感信息(如密码、Token),因为 JS 代码在客户端是可见的。

总结

实现路径就是:ArkUI 按钮点击 -> JSON.stringify(Map) -> Web组件.runJavaScript(“回调函数(数据)”) -> Web页面对象接收并处理

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

这个思路可以,但不要把 JSON 直接拼成 receiveDataFromArk(‘jsonString’) 这类单引号字符串;遇到单引号、换行、反斜杠就可能打断 JS,外部数据还会有注入风险。建议:const payload = JSON.stringify(data); const script = ‘window.receiveDataFromArkTS(’ + payload + ‘)’; this.controller.runJavaScript(script); Web 侧 receiveDataFromArkTS(data) 直接拿对象。调用放在 onPageEnd/loadUrl 完成后,并开启 javaScriptAccess(true)。不可信页面要固定函数名/白名单。

简单的一次性传值用 WebviewController.runJavaScript() 就可以,但要注意 JSON 字符串注入问题,别直接手拼。建议先 JSON.stringify(data),再作为 JS 函数参数传入:this.controller.runJavaScript(window.receiveFromArkTS(${JSON.stringify(data)}))。Web 侧定义 window.receiveFromArkTS = (data) => { … },这里拿到的就是对象。若数据量较大或需要 ArrayBuffer,可以看 runJavaScriptExt();若后续要双向通信、多个方法调用、回调结果,建议用 javaScriptProxy 建一层通信桥,而不是每个按钮都拼 JS 字符串。

这个还是很简单的,鸿蒙里 ArkUI ↔ Web 通信就用官方的:

webviewController.runJavaScript()

你要传 JSON / Map,直接转成字符串丢进去就行,网页那边自动解析成对象。

@Entry
@Component
struct WebToArkTSDemo {
  // Web 控制器
  webController: WebViewController = new WebViewController();

  // 你要发送的 JSON / Map 数据
  sendDataToWeb() {
    // 1. 定义 JSON 对象(支持嵌套、数组、任意结构)
    const data = {
      name: "鸿蒙设备",
      deviceId: "123456",
      status: true,
      list: [1, 2, 3],
      info: {
        temperature: 25,
        humidity: 60
      }
    };

    // 2. 转成 JSON 字符串(关键)
    const jsonStr = JSON.stringify(data);

    // 3. 调用网页的 JS 方法,把 JSON 传过去
    this.webController.runJavaScript(`receiveDataFromHarmony(${jsonStr})`);
  }

  build() {
    Column() {
      // Web 组件
      Web({
        src: $rawfile("index.html"), // 本地网页
        controller: this.webController
      })
      .width("100%")
      .height(500)

      // 按钮:发送数据
      Button("点击发送JSON给Web")
        .onClick(() => {
          this.sendDataToWeb();
        })
        .margin(10)
    }
  }
}

可以使用双端通信 ArkWeb渲染框架适配-ArkWeb-应用框架 - 华为HarmonyOS开发者 详细看这个文档

cke_682.png

通信层:对上层屏蔽具体的通信机制,主要负责Web侧和ArkTS侧数据的传递,但不解析数据的业务含义,不关注传递的数据内容。数据可以序列化为字符串进行传递或者以object对象进行传递。使用javaScriptProxy代理机制实现的通信层

通道层(Channel):允许注册多种方法层通道。该层的JS侧实现负责把方法层的API信息对象(包含名称、参数、返回值类型等信息)打包成通信层识别的信息数据,交给通信层传递到ArkTS侧。ArkTS侧的实现包含两个主要功能,一个是把信息数据解包出API的信息,并交给ArkTS侧的方法层调用具体的API;另外一个功能就是执行jsCall,ArkTS侧通过WebviewController .runJavaScript()方法在执行JS侧的回调函数

在HarmonyOS Next中,向Web组件传递JSON格式的Map数据,需先将Map转换为普通对象再序列化为JSON字符串。使用JSON.stringify(Object.fromEntries(map))生成字符串,然后通过WebviewControllerrunJavaScriptevaluateJavaScript方法注入到Web端。Web端需定义接收函数的全局方法。注意避免直接传递Map对象。

在HarmonyOS Next中,向Web组件传递JSON数据常用两种方式:

  1. 调用runJavaScript直接执行JS
    将JSON对象转为字符串,拼接成JS调用。

    // ArkUI侧
    let jsonStr = JSON.stringify({key: "value"});
    this.webController.runJavaScript(`window.receiveData(${jsonStr})`);
    

    Web侧需预先定义接收函数:

    window.receiveData = (data) => { console.log(data); };
    
  2. 通过JavaScriptProxy注入对象(更规范)
    先注册代理对象,然后从ArkUI侧调用代理方法传参。

    // ArkUI注册
    this.webController.javaScriptProxy.register({
      sendData: (jsonStr: string) => {
        this.webController.runJavaScript(`window.receiveData(${jsonStr})`);
      }
    }, 'nativeProxy', ['sendData']);
    

    按钮事件中调用:

    this.webController.javaScriptProxy.call('nativeProxy', 'sendData', [JSON.stringify(mapData)]);
    

    两种方式都需要Web侧提前定义好接收函数。建议数据量小时用runJavaScript,接口较多时用javaScriptProxy

回到顶部