鸿蒙 ArkTs 的WebView如何与JS交互

c 复制代码
import webView from '@ohos.web.webview';
import { BusinessError } from '@kit.BasicServicesKit';
import { picker } from '@kit.CoreFileKit';

@Component
export struct WebViewComponent {

  webviewController: webView.WebviewController = new webView.WebviewController();
  // 页面 loading
  isLoading: boolean = true
  // 链接
  @Prop @Require url: string = 'https://blog.csdn.net/survivorsfyh'
  // 声明注册JS侧调用鸿蒙侧交互对象
  private webCallApp: WebCallApp = new WebCallApp(this.webviewController)
  // 与JS端约定的交互协议名Key
  private proxyName:string = 'Harmony'

  onBackPress(): boolean | void { // Web 组件左滑手势返回处理
    if (this.webviewController.accessStep(-1)) {
      // 返回上一层级
      this.webviewController.backward()
      // 执行自定义逻辑
      return true
    } else { // 执行系统默认返回逻辑
      return false
    }
  }

  webDebugMethod(): void {
    try {
      // 启用网页调试功能
      webView.WebviewController.setWebDebuggingAccess(true);
    } catch (error) {
      let e: BusinessError = error as BusinessError;
      console.log(`[Web] ****** Error Code: ${e.code}, Message: ${e.message}`);
      this.webviewController.refresh(); // 页面异常,刷新
    }
  }

  aboutToDisappear(): void {
     // javaScriptProxy接口需要和deleteJavaScriptRegister接口配合使用,防止内存泄漏。
     this.webviewController.deleteJavaScriptRegister(this.proxyName)
  }

  build() {
    if (this.url) {
      Column(){
        Button('runJavaScript方法调用js方法')
          .onClick(() => {
            if (this.webviewController) {
              /**
               * 1.注意:传参时要注意一个问题,就是这里的调用这个方法,是直接写字符串去调用.
               * 但是如果要在调用方法里边传参,且正好是个字符串,就需要区分双引号和单引号,
               * 保证对runJavaScript穿的方法收尾对应的是一组符号,而方法内部传参对应的是一组符号,
               * 示范如下:
               * 2."changeMessage"是一个定义在JS端,参数入参为一个字符串的函数方法.
               * */
              this.webviewController.runJavaScript('changeMessage("调用h5方法成功")')
            }
          })
        Button('鸿蒙调用js,传递js代码块,修改颜色字体')
          .onClick(() => {
            // 传递runJavaScript侧代码方法,也可以像这样直接将整个JS方法体传递过去以达到我们修改颜色的目的.
            this.webviewController.runJavaScript(`function changeColor(){document.getElementById('text').style.color = 'red'}`);
          })
        /// 白屏情况可能是权限导致,把权限全部开启,例如本地存储或者 http & https
        Web({ src: this.url, controller: this.webviewController })
          .width('100%')
          .height('100%')
          .backgroundColor(Color.White)
          .multiWindowAccess(true)
          .javaScriptAccess(true)// 访问本地资源文件
          .imageAccess(true)// 权限状态开启
          .onlineImageAccess(true)// 权限状态开启
          .fileAccess(true)// 权限状态开启
          .domStorageAccess(true)// 数据存储权限
          .mediaPlayGestureAccess(true)// 媒体权限
          .mixedMode(MixedMode.All)// https 加载
          .layoutMode(WebLayoutMode.FIT_CONTENT)// 自适应布局
          .verticalScrollBarAccess(true)// 滚动条
          .horizontalScrollBarAccess(false)// 滚动条
          .cacheMode(CacheMode.Default)// 缓存
          .zoomAccess(false)// 禁止手势缩放
          .geolocationAccess(true)// 定位权限
          .onConsole((event) => {
            console.log('[交互] - onConsole')
            console.info(event?.message.getMessage())
            return false
          })
          .onPageBegin(() => { // 页面加载中
            console.log('[Web] - 页面加载中:', this.url)
          })
          .onPageEnd(() => { // 页面加载完成
            console.log('[Web] - 页面加载完成:', this.url)
            this.isLoading = false
          })
          .onErrorReceive((event) => { // 异常: 无网络,页面加载错误时
            if (event) {
              console.info('getErrorInfo:' + event.error.getErrorInfo());
              console.info('getErrorCode:' + event.error.getErrorCode());
              console.info('url:' + event.request.getRequestUrl());
              console.info('isMainFrame:' + event.request.isMainFrame());
              console.info('isRedirect:' + event.request.isRedirect());
              console.info('isRequestGesture:' + event.request.isRequestGesture());
              console.info('getRequestHeader_headerKey:' + event.request.getRequestHeader().toString());
              let result = event.request.getRequestHeader();
              console.info('The request header result size is ' + result.length);
              for (let i of result) {
                console.info('The request header key is : ' + i.headerKey + ', value is : ' + i.headerValue);
              }
            }
          })
          .onHttpErrorReceive((event) => { // 异常: 网页加载资源 Http code >= 400 时
            if (event) {
              console.info('url:' + event.request.getRequestUrl());
              console.info('isMainFrame:' + event.request.isMainFrame());
              console.info('isRedirect:' + event.request.isRedirect());
              console.info('isRequestGesture:' + event.request.isRequestGesture());
              console.info('getResponseData:' + event.response.getResponseData());
              console.info('getResponseEncoding:' + event.response.getResponseEncoding());
              console.info('getResponseMimeType:' + event.response.getResponseMimeType());
              console.info('getResponseCode:' + event.response.getResponseCode());
              console.info('getReasonMessage:' + event.response.getReasonMessage());
              let result = event.request.getRequestHeader();
              console.info('The request header result size is ' + result.length);
              for (let i of result) {
                console.info('The request header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
              }
              let resph = event.response.getResponseHeader();
              console.info('The response header result size is ' + resph.length);
              for (let i of resph) {
                console.info('The response header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
              }
            }
          })
          .onAlert((event) => { // 提示框处理相关
            AlertDialog.show({
              title: '温馨提示',
              message: event?.message,
              confirm: {
                value: '确认',
                action: () => {
                  event?.result.handleConfirm()
                }
              },
              cancel: () => {
                event?.result.handleCancel()
              }
            })
            return true;
          })
          .onConfirm((event) => { // 提示框处理相关
            AlertDialog.show({
              title: '温馨提示',
              message: event?.message,
              confirm: {
                value: '确认',
                action: () => {
                  event?.result.handleConfirm()
                }
              },
              cancel: () => {
                event?.result.handleCancel()
              }
            })
            return true
          })
          .onShowFileSelector((event) => { // 文件选择处理
            console.log('MyFileUploader onShowFileSelector invoked');
            const documentSelectOptions = new picker.PhotoSelectOptions();
            let uri: string | null = null;
            const documentViewPicker = new picker.PhotoViewPicker();
            documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
              uri = documentSelectResult[0];
              console.info('documentViewPicker.select to file succeed and uri is:' + uri);
              if (event) {
                event.result.handleFileList([uri]);
              }
            }).catch((err: BusinessError) => {
              console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
            })
            return true
          })
          .onLoadIntercept((event) => {
            if (event.data.isRedirect()) {
              console.log('[重定向]');
            }
            return false
          })
            /// JS调用ArkTS,web组件初始化调用
            /*
             * 将javaScriptProxy中的ArkTS对象注册到Web组件中,该对象将使用JavaScriptProxy中指定的名称注册到网页的所有框架中
             * 这使得JavaScript可以调用javaScriptProxy中ArkTS对象的方法。
             * */
          .javaScriptProxy({
            //将交互对象注入web
            object: this.webCallApp,
            //与JS端的交互协议Key
            name: this.proxyName,
            // 基于 object 的 WebCallApp 中交互具体实现的若干函数方法
            // 交互对象中所涵盖的方法,支持多个.JS调用鸿蒙App端的方法,最终方法会在交互对象定义的方法中触发
            methodList: WebCallApp.methodList,
            // 参与注册的应用侧JavaScript对象的异步方法。但是异步方法无法获取返回值。
            asyncMethodList:WebCallApp.asyncMethodList,
            controller: this.webviewController
          })
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Start)
    } else {
      // 加载异常,请重试
    }
  }

}

/**
 * javaScriptProxy和registerJavaScriptProxy异同
 *
 * 相同点:二者都可以注入JavaScript对象到window对象中,并在window对象中调用该对象的方法.
 *
 * 不同点:
 * 1.从注册对象的角度来看,前者仅支持注册一个对象,而后者支持注册多个对象。
 * 2.从生命周期上讲,javaScriptProxy在Web组件初始化调用,registerJavaScriptProxy在Web组件初始化完成后调用。
 * 3.从接口角度来看,javaScriptProxy是Web组件的方法,而registerJavaScriptProxy是WebviewController的方法。
 * */

class WebCallApp {

  private controller: webView.WebviewController;
  constructor(controller: WebviewController) {
    this.controller = controller
  }

  /// JS调用ArkTS 同步方法数组,包含同步方法的方法名.
  static methodList:string[] = [
    'WebCallApp',
  ]
  static asyncMethodList:string[] = [
    'WebTestAsync',
    'WebTest',
  ]

  WebCallApp(value: string) {

  }
  async WebTestAsync(value: string):Promise<string>{
    return '测试 - 异步'
  }
  WebTest(value: string): Promise<string> { // 测试 - promise
    return new Promise((resolve, reject) => {
      try {
        resolve('[Callback]:你好')
      } catch (e) {
        reject('[Callback]:异常')
      }
    })
  }

}

参考链接

修改问题:避免web不适配,需要做下面修改

相关推荐
遇到困难睡大觉哈哈2 小时前
HarmonyOS支付接入证书准备与生成指南
华为·harmonyos
叫我詹躲躲2 小时前
别再用mixin了!Vue3自定义Hooks让逻辑复用爽到飞起
javascript·vue.js
赵浩生2 小时前
鸿蒙技术干货10:鸿蒙图形渲染基础,Canvas绘图与自定义组件实战
harmonyos
赵浩生2 小时前
鸿蒙技术干货9:deviceInfo 设备信息获取与位置提醒 APP 整合
harmonyos
豆苗学前端2 小时前
HTML + CSS 终极面试全攻略(八股文 + 场景题 + 工程落地)
前端·javascript·面试
珑墨3 小时前
【迭代器】js 迭代器与可迭代对象终极详解
前端·javascript·vue.js
Fantastic_sj3 小时前
[代码例题] var 和 let 在循环中的作用域差异,以及闭包和事件循环的影响
开发语言·前端·javascript
BlackWolfSky3 小时前
鸿蒙暂未归类知识记录
华为·harmonyos
JANG10243 小时前
【Linux】常用指令
linux·服务器·javascript