鸿蒙开发——解耦jsbridge和webview

背景

当公司有一批产品矩阵的时候,我们在做基础的har包构建的时候也是希望把webview进行组件化的,考虑到老的项目端和h5经常有非常丰富的交互jsBridge,那么如何优雅的把webview封装到har包中,而不造成和业务视图或者逻辑的耦合呢?答案是虚类,ArkTs是支持面向对象的,因此我们可以根据虚类的方式,让jsbridge类在业务代码中实现,而在webview初始化的时候将jsbridge的对象实例化并在特定时刻注入进去。

实现

  1. 如何进行jsbridge的实现,这个官方有文档,大家可以自行去搜索,这里展示一下代码
typescript 复制代码
Web({ src: "", controller: this.controller })
          .domStorageAccess(true)
          .javaScriptAccess(true)
          .zoomAccess(false)
          .onControllerAttached(() => {
            //注册代理
            if (this.diyJSExportObject) {  this.controller.registerJavaScriptProxy(this.diyJSExportObject.getInstance(this.pathStack,this.controller), "diyJSExportObject", ["jsCallback"])
            }
            // 进入时初始化
            this.controller.loadUrl(this.url);
          })

可以看到,我们通过webcontroller的registerJavaScriptProxy方法为webview环境注入了一个全局对象diyJSExportObject,这个对象只有一个方法jsCallback,然后我们所有的客户端和h5的交互都通过window.diyJSExportObject.jsCallback进行交互,因此这里需要和webview端做一些约定,具体如何约定可以参考之前的一篇文章Vue3桥接composition api

  1. 如何声明和定义diyJSExportObject

首先我们想要解耦,那么这个对象的实例化,肯定不能在har包中,因此我们需要通过参数将实例化对象传入,但是我们不同的业务可能命名的类都不一样,而且类也不能从业务进行引入,因此我们就采用了虚基类的方法进行声明和对象化。

typescript 复制代码
diyJSExportObject: DIYJSExportObject | null = null;
  1. DIYJSExportObject的实现

显然我们需要实现一个jsCallback方法以供web测调用,然后要实现一个端测调用web测的方法,可以根据上面代码看到我们需要进行pathStack和controller的传递,因此DIYJSExportObject的实现要至少有这两个参数,然后因为实例需要传入pathStack和controller,我们不能通过构造函数直接获取,因此需要提供一个动态获取实例的方法getInstance,那么DIYJSExportObject的实现即可构建为:

typescript 复制代码
export abstract class DIYJSExportObject {
  abstract pathStack: NavPathStack;
  abstract controller: webview.WebviewController;
  // 端测调用web测的方法
  abstract nativeCallJs(actionName: string, data: CommonObj): void;
  abstract jsCallback(callee: string): void;
  abstract getInstance(pathStack: NavPathStack, controller: webview.WebviewController): DIYJSExportObject
}
  1. DIYJSExportObject的实现类,业务侧

业务侧我们将进行DIYJSExportObject的实现,该类可以进行一些前后端的交互,比如互相调用方法,互相调用组件,支持webview测进行分享,图片保存,缓存等等其他高端的端侧功能,一般的我们会约定不同的id来对应不同的功能:

typescript 复制代码
export class TESTJSExportObject extends DIYJSExportObject {
  pathStack: NavPathStack;
  controller: webview.WebviewController;
  constructor() {
    super()
    this.pathStack = new NavPathStack();
    this.controller = new webview.WebviewController;
  }

  getInstance(pathStack: NavPathStack, controller: webview.WebviewController) {
    this.pathStack = pathStack;
    this.controller = controller;
    return this
  }


  nativeCallJs(funName: string, data: CommonObj) {
    this.controller.runJavaScript(
      `${funName}(${JSON.stringify(JSON.stringify(data))})`,
      (error, result) => {
        if (error) {
          console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
          return;
        }
        if (result) {
          console.info(`The nativeCallJs() return value is: ${result}`);
        }
      })
  }

  jsCallback(callee: string) {
    let actionJson: JSONObject = JSONObject.parse(callee)
    let action = actionJson.get("actionId");
    switch (action) {
      case 'getClientInfo':
        this.getClientInfo();
        break;
    }
  }

  private getClientInfo() {

  }
}

这里实现了业务侧的TESTJSExportObject,它是DIYJSExportObject的实现类

5.初始化webview组件

har包中我们的组件大概这样构建:

typescript 复制代码
@Component
export struct DiyWebview {
  @Consume('pathStack') pathStack: NavPathStack
  controller: webview.WebviewController = new webview.WebviewController
  diyJSExportObject: DIYJSExportObject | null = null;
  build() {
    NavDestination() {
      Column() {
      Web({ src: "", controller: this.controller })
          .domStorageAccess(true)
          .javaScriptAccess(true)
          .zoomAccess(false)
          .onControllerAttached(() => {
            //注册代理
            if (this.diyJSExportObject) {  this.controller.registerJavaScriptProxy(this.diyJSExportObject.getInstance(this.pathStack,this.controller), "diyJSExportObject", ["jsCallback"])
            }
            // 进入时初始化
            this.controller.loadUrl(this.url);
          })
  }
}

那么我们的调用,可以这样写:

typescript 复制代码
import { DiyWebview } from '@library'
import { TESTJSExportObject } from './testJSExportObject'

DiyWebview({
   diyJSExportObject: new TESTJSExportObject()
})

以上即完成了Webview组件和jsbridge的解耦,webview的基本功能在har包,而和webview的交互功能都在业务侧,这样即可方便webview获得端测的丰富的能力。

总结

构建产品矩阵的通用库是一件有意义且漫长的过程,鸿蒙之路才刚开始,期待未来百花齐放。本文中的代码大部分为精简源码,部分伪码,大家理解为主,自行实现,感谢阅读。

相关推荐
雯0609~15 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ18 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z24 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
天天扭码38 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4041 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue
临枫5411 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript