harmony 鸿蒙Web开发常见问题

  • 2023-06-24
  • 浏览 (2566)

Web开发常见问题

H5页面如何与ArkTS交互(API 10)

问题现象

目前javaScriptProxy仅支持同步调用,异步调用无法获取执行结果。

解决措施

对javaScriptProxy和runJavaScript封装,实现JSBridge通信方案。适用于H5调用原生侧函数。使用Web组件javaScriptProxy将原生侧接口注入到H5的window对象上,通过runJavaScript接口执行JS脚本到H5中,并在回调中获取脚本执行结果。具体调用流程如下图所示:

image5

  • 首先通过Web组件的javaScriptProxy属性,将JSBridgeHandle对象注册到H5的window上,作为H5调用原生的通道。当H5开始加载时,在onPageBegin生命周期中调用initJSBridge()方法初始化JSBridge。 // javaScriptProxy对象 public get javaScriptProxy() { return { object: { call: this.call }, name: "JSBridgeHandle", methodList: ['call'], controller: this.controller, } }
  // 使用Web组件加载H5页面
  @Component
  struct JsProxy {
    private controller: WebviewController = new WebView.WebviewController()
    private jsBridge: JSBridge = new JSBridge(this.controller)
    build() {
      Column(){
        Web({ src: $rawfile('index.html'), controller: this.controller })
          .javaScriptProxy(this.jsBridge.javaScriptProxy)
          .onPageBegin(() => {
            this.jsBridge.initJSBridge()
          })
      }
    }
  }
  • 在initJSBridge方法中,通过webviewControll.runJavaScript()将JSBridge初始化脚本注入H5执行。当H5调用时,生成window.callID标识回调函数,将callID与调用参数使用JSBridgeHandle.call传到原生侧。通过JSBridgeCallback接收原生侧执行的结果,根据callID找到对应callback执行并且释放内存。

    // bridgeKey与bridgeMethod动态生成H5侧调用的入口
    bridgeKey: string = 'JSBridge'
    bridgeMethod: string = 'call'
    // 初始化脚本注入H5侧
    public initJSBridge() {
      try {
          this.controller.runJavaScript(`
              // 接收原生侧结果,执行callback
              function JSBridgeCallback(id, params){
                  window.JSBridgeMap[id](params)
              };
              // 声明调用入口
              window.${this.bridgeKey} = {
                  ${this.bridgeMethod}(method, params, callback){
                      window.JSBridgeMap[id] = callback||(() => {});
                      JSBridgeHandle.call(method, JSON.stringify(paramsObj));
                  },
              }`)
      }
    }
    
  • JSBridgeHandle.call()是H5调用原生接口的统一入口,在该方法中根据H5调用的方法名,匹配到对应接口去调用。调用结束后通过this.callback()方法将调用结果返回H5。callback方法中使用webviewControll.runJavaScript()调用H5的JSBridgeCallback回传callID和调用结果。 “` // call方法调用原生侧方法,接收结果 private call = (fun, params) => { try { const paramsObj = JSON.parse(params) const events = this.exposeManage.methodMap.get(fun) const results = [] events.forEach(callFun => { results.push(callFun(paramsObj.data)) }) Promise.all(results.filter(i => !!i)).then(res => { this.callback(paramsObj.callID, res.length > 1 ? res : res[0]) }) } }

// 通过runJavaScript调用JSBridgeCallback执行回调 private callback(id, data) { this.controller.runJavaScript(__JSBridgeCallback__("${id}", ${JSON.stringify(data)})); }



## Web组件的onUrlLoadIntercept返回结果是否影响onInterceptRequest(API 9)

**解决措施**

Web组件的onUrlLoadIntercept的不同返回结果对应不同的操作:

- onUrlLoadIntercept返回true则直接拦截URL请求。

- onUrlLoadIntercept返回false走onInterceptRequest回调。

**参考链接**

[onUrlloadIntercept](../reference/arkui-ts/ts-basic-components-web.md#onurlloadinterceptdeprecated)


## 为什么Web组件的onKeyEvent键盘事件不生效(API 9)

**问题现象**

Web组件设置onKeyEvent监听键盘事件,键盘按下或抬起该事件不触发。

**解决措施**

onKeyEvent为通用事件,当前Web组件不支持通用事件。Web组件监听键盘事件可以使用onInterceptKeyEvent回调函数。

**参考链接**

[onInterceptKeyEvent](../reference/arkui-ts/ts-basic-components-web.md#oninterceptkeyevent9)


## onInterceptRequest拦截URL并自定义HTML文件,页面加载失败(API 9)

**问题现象**

onInterceptRequest拦截页面Web的src的链接后返回自定义HTML,但是自定义HTML文件里面的script标签里的内容没有加载。

**解决措施**

设置拦截器时,如果只设置setResponseData,内核将无法识别到这是个HTML文件,需要同时设置setResponseEncoding、setResponseMimeType、setResponseHeader等参数。

**代码示例**

Web({ src: ‘www.example.com’, controller: this.controller }) .onInterceptRequest((event) => { console.log(‘url:’ + event.request.getRequestUrl()) this.responseweb = new WebResourceResponse(); var head1:Header = { headerKey:“Connection”, headerValue:“keep-alive” } var length = this.heads.push(head1) this.responseweb.setResponseHeader(this.heads) this.responseweb.setResponseData(this.webdata) this.responseweb.setResponseEncoding(‘utf-8’) this.responseweb.setResponseMimeType(‘text/html’) this.responseweb.setResponseCode(200) this.responseweb.setReasonMessage(‘OK’) return this.responseweb })


**参考链接**

[WebResourceResponse](../reference/arkui-ts/ts-basic-components-web.md#webresourceresponse)


## 如何在ArkTS代码中执行HTML内的JS函数(API 9)

**解决措施**

通过WebviewController中runJavaScript方法异步执行JavaScript脚本,并通过回调方式获取执行结果。

> **说明:**
> runJavaScript需要在loadUrl完成后,比如onPageEnd中调用。

**参考链接**

[runJavaScript](../reference/apis/js-apis-webview.md#runjavascript)


## 使用Web组件加载本地网页时,如何在本地网页中调用ArkTS中的函数(API 9)

**解决措施**

1. 准备一个html文件,例如:

<!DOCTYPE html> Document

标题


2. 在ArkTs中使用JavaScriptProxy方法将ArkTs里的对象注册到H5的window对象中,然后在h5中使用window对象调用该方法。比如下面例子,在ArkTs中将testObj这个对象以别名objName注册到h5的window对象上,在上面的h5中就可以使用window.objName去访问这个对象。

// xxx.ets import web_webview from ‘@ohos.web.webview’ @Entry @Component struct Index { @State message: string = ‘Hello World’ controller: web_webview.WebviewController = new web_webview.WebviewController() testObj = { test: (data1, data2, data3) => { console.log(“data1:” + data1); console.log(“data2:” + data2); console.log(“data3:” + data3); return “AceString”; }, toString: () => { console.log(‘toString’ + “interface instead.”); } } build() { Row() { Column() { Web({ src:$rawfile(‘index.html’), controller:this.controller }) .javaScriptAccess(true) .javaScriptProxy({ object: this.testObj, name: “objName”, methodList: [“test”, “toString”], controller: this.controller, }) } .width(‘100%’) } .height(‘100%’) } }


**参考链接**

[javaScriptProxy](../reference/arkui-ts/ts-basic-components-web.md#javascriptproxy)


## Web组件domStorageAccess属性设置(API 9)

**解决措施**

设置是否开启文档对象模型存储接口(DOM Storage API)权限,默认未开启,控制web网页中localStorage的使用,对sessionStorage未做控制

**参考链接**

[domStorageAccess](../reference/arkui-ts/ts-basic-components-web.md#domstorageaccess)


## 如何解决Web组件加载的HTML页面内检测网络状态失败(API 9)

**问题现象**

在HTML页面内通过window.navigator.onLine获取网络状态,联网和断网情况下均为false。

**解决措施**

配置应用获取网络信息权限: ohos.permission.GET_NETWORK_INFO

**参考链接**

[GET\_NETWORK\_INFO](../security/permission-list.md#ohospermissionget_network_info)


## 如何自定义拼接设置UserAgent参数(API 9)

**解决措施**

默认UserAgent需要通过WebviewController获取。WebviewController对象必须在Web组件绑定后,才能调用WebviewController上的方法getUserAgent获取默认UserAgent。因此在页面加载前通过自定义字符串拼接修改UserAgent,可采用此方式:

1. 使用\@State定义初始UserAgent,绑定到Web组件;

2. 在Web组件的onUrlLoadIntercept回调中,通过WebviewController.getUserAgent()获取默认UserAgent,并修改Web组件绑定的UserAgent

**代码示例**

import web_webview from ‘@ohos.web.webview’ @Entry @Component struct Index { private controller: web_webview.WebviewController = new web_webview.WebviewController() @State userAgentPa: string = “ build() { Row() { Column() { Web({ src: ‘http://www.example.com’, controller: this.controller }) //需要手动替换为真实网站 .width(‘100%’) .userAgent(this.userAgentPa) .onUrlLoadIntercept((event) => { let userAgent = this.controller.getUserAgent(); this.userAgentPa = userAgent + ‘ 111111111’ return false; }) } .width(‘100%’) } .height(‘100%’) } }


**参考链接**

[userAgent](../reference/arkui-ts/ts-basic-components-web.md#useragentdeprecated)、[getUserAgent](../reference/apis/js-apis-webview.md#getuseragent)


## Web组件中如何通过手势滑动返回上一个Web页面(API 9)

**解决措施**

通过重写onBackPress函数来自定义返回逻辑,使用WebviewController判断是否返回上一个Web页面。

**示例代码**

import web_webview from ‘@ohos.web.webview’; @Entry @Component struct Index { controller: web_webview.WebviewController = new web_webview.WebviewController(); build() { Column() { Web({ src: ‘http://www.example.com’, controller: this.controller })//需要手动替换为真实网站 } } onBackPress() { // 当前页面是否可前进或者后退给定的step步(-1),正数代表前进,负数代表后退 if (this.controller.accessStep(-1)) { this.controller.backward(); // 返回上一个web页 // 执行用户自定义返回逻辑 return true } else { // 执行系统默认返回逻辑,返回上一个page页 return false } } } “`

参考链接

accessStep

你可能感兴趣的鸿蒙文章

harmony 鸿蒙常见问题

harmony 鸿蒙如何在CMake工程中使用NDK

harmony 鸿蒙程序访问控制开发常见问题

harmony 鸿蒙应用模型常见问题

harmony 鸿蒙ArkUI动画/交互事件开发常见问题(ArkTS)

harmony 鸿蒙ArkTS语法使用常见问题

harmony 鸿蒙ArkUI组件开发常见问题(ArkTS)

harmony 鸿蒙ArkUI框架开发常见问题(JS)

harmony 鸿蒙ArkUI布局开发常见问题(ArkTS)

harmony 鸿蒙ArkUI路由/导航开发常见问题(ArkTS)

0  赞