JSBridge 之 Web 端與 App 交互 - 鴻蒙 HarmonyOS Next

前端 JavaScript 與鴻蒙 Web 組件間相互通信的方式;

注:此案例采用兩端間雙向模式機(jī)制進(jìn)行通信;

單向模式可參考 前端 Web 與原生應(yīng)用端 WebView 通信交互
官方文檔傳送門如下:
WebCall
AppCallWeb

一.鴻蒙端

1.設(shè)置 WebCallApp 方法

import WebView from '@ohos.web.webview';
import business_error from '@ohos.base'
import { BusinessError } from '@kit.BasicServicesKit';
 
// 聲明注冊對象
@State WebCallAppMethod: WebCallAppClass = new WebCallAppClass(this.controller)
 
 
// WebCallApp 類方法
class WebCallAppClass {
  private webView: WebView.WebviewController;
 
  constructor(webView: WebviewController) {
    this.webView = webView
  }
 
  // 方式一: 方法無回調(diào),通過交互協(xié)議 WebCallApp => AppCallWeb
  WebCallApp(value: string) {
    console.log('********* [交互] --- 協(xié)議 - HWebView')
    // 接收 Web 端數(shù)據(jù)源
    console.log(value)
 
 
    // 交互數(shù)據(jù)處理
    do something ...
 
    // 如果需要數(shù)據(jù)回調(diào),則通過如下 callback 方法將處理后的數(shù)據(jù)回調(diào)至 Web 
    // 如果不需要數(shù)據(jù)回調(diào),則執(zhí)行其它業(yè)務(wù)邏輯即可,無需調(diào)用如下 callback 方法
    this.webView.runJavaScript(value)
  }
 
  // 方式二: 方法回調(diào),WebCallApp 直接 return
  // WebCallApp(value: string): string { // 直接 return 方式
  //   console.log('[交互] --- WebCallApp - 測試 - HWebView')
  //   // 接收 Web 端數(shù)據(jù)源
  //   console.log(value)
  //
  //   // 交互數(shù)據(jù)處理
  //   do something ...
  //
  //   // callback
  //   return value
  // }
 
  WebTestAsync = async (value: string): Promise<string> => { // 測試 - 異步
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    let commandManager = CommandManager.shareInstance()
    let res = await commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let argsJson = JSON.stringify(res.args)
    console.log('[WebTestAsync]')
    console.log(argsJson)
    return argsJson
  }
 
  WebTest(value: string): Promise<string> { // 測試 - promise
    let p: Promise<string> = new Promise((resolve, reject) => {
      let userInfo: UserInfoModal = JSON.parse(userInfoData)
      userInfo.instituteId = instituteId
      userInfo.genderCode = genderCode
      let commandManager = CommandManager.shareInstance()
      let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
      let argsJson = JSON.stringify(res.args)
      resolve('[Callback]:' + argsJson)
    })
    return p
  }
}

2.基于 @ohos.web.webview 初始化并配置

通過 javaScriptProxy 的代理方法與前端通信接發(fā)消息;

1).name 兩端交互的協(xié)議 key

2).object 對應(yīng)的是 WebCallApp 的交互接收方法

3).methodList 基于 object 的 WebCallApp 中交互具體實現(xiàn)的若干函數(shù)方法

4).controller 組件

Web({ src: this.url, controller: this.controller })
  .width('100%')
  .height('100%')
  .backgroundColor(Color.White)
  .multiWindowAccess(true)
  .javaScriptAccess(true)// 訪問本地資源文件
  .imageAccess(true)// 權(quán)限狀態(tài)開啟
  .onlineImageAccess(true)// 權(quán)限狀態(tài)開啟
  .fileAccess(true)// 權(quán)限狀態(tài)開啟
  .domStorageAccess(true)// 數(shù)據(jù)存儲權(quán)限
  .mediaPlayGestureAccess(true)// 媒體權(quán)限
  .mixedMode(MixedMode.All)// https 加載
  .layoutMode(WebLayoutMode.FIT_CONTENT)// 自適應(yīng)布局
  .verticalScrollBarAccess(true)// 滾動條
  .horizontalScrollBarAccess(false)// 滾動條
  .cacheMode(CacheMode.Default)// 緩存
  .zoomAccess(false)// 禁止手勢縮放
  .geolocationAccess(true)// 定位權(quán)限
  .onConsole((event) => {
    console.log('[交互] - onConsole')
    LogUtils.info(event?.message.getMessage())
    return false
  })
  .onPageBegin(() => { // 頁面加載中
    console.log('[Web] - 頁面加載中:', this.url)
  })
  .onPageEnd(() => {
    console.log('[Web] - 頁面加載完成:', this.url)
    this.isLoading = false
  })
  .onErrorReceive((event) => { // 異常: 無網(wǎng)絡(luò),頁面加載錯誤時
    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) => { // 異常: 網(wǎng)頁加載資源 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) => { // 提示框處理相關(guān)
    AlertDialog.show({
      title: '溫馨提示',
      message: event?.message,
      confirm: {
        value: '確認(rèn)',
        action: () => {
          event?.result.handleConfirm()
        }
      },
      cancel: () => {
        event?.result.handleCancel()
      }
    })
    return true;
  })
  .onConfirm((event) => { // 提示框處理相關(guān)
    AlertDialog.show({
      title: '溫馨提示',
      message: event?.message,
      confirm: {
        value: '確認(rèn)',
        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
  })
  .javaScriptProxy({
    // web call app, web 組件初始化調(diào)用
    // 對象注入 web
    object: this.WebCallAppMethod,
    name: 'WebCallAppHarmony', // AppCallWeb
    methodList: ['WebCallApp', 'WebTest', 'WebTestSync', 'WebTestAsync'],
    controller: this.controller
  })

3.完整案例

import HeaderComponent from "../../components/NavigationBar"
import WebView from '@ohos.web.webview';
import business_error from '@ohos.base'
import { BusinessError } from '@kit.BasicServicesKit';
import CommandManager from '../../expand/utility/CommandManager'
import AppCallWeb, { DataType } from '../../expand/utility/AppCallWeb'
 
 
// 路由取值
const routerParams = router.getParams() as RouterModel
 
// 用戶信息相關(guān)
let userID: string = ''
let token: string = ''
let instituteId: string = ''
let cellphone: string = ''
let userInfoData: string = ''
 
@Entry
@Component
struct HWebView {
  controller: WebView.WebviewController = new WebView.WebviewController();
  
  // setCookies
  headers: Array<WebView.WebHeader> = [{ headerKey: 'author', headerValue: 'survivors' }];
  // 頁面 loading
  isLoading: boolean = true
  // 聲明注冊對象
  @State WebCallAppMethod: WebCallAppClass = new WebCallAppClass(this.controller)
  // 標(biāo)題
  @State title: string = ''
  // 導(dǎo)航欄是否顯示(默認(rèn)顯示 true)
  @State navHidden: boolean = true
  // 鏈接
  @State url: string = 'https://blog.csdn.net/survivorsfyh'
 
  aboutToAppear(): void { // 頁面即將渲染
    console.log('[WebView] - 頁面即將渲染');
    // 路由取值
    console.log(JSON.stringify(routerParams))
    const title = routerParams.title
    const navHidden = routerParams.navHidden
    const link = routerParams.link
    this.title = title ? title : ''
    this.navHidden = navHidden ? navHidden : false
    this.url = link ? link : ''
 
    this.webDebugMethod()
  }
 
  aboutToDisappear(): void { // 頁面即將銷毀
    console.log('[WebView] - 頁面即將銷毀');
  }
 
  onBackPress(): boolean | void { // Web 組件左滑手勢返回處理
    if (this.controller.accessStep(-1)) {
      // 返回上一層級
      this.controller.backward()
      // 執(zhí)行自定義邏輯
      return true
    } else { // 執(zhí)行系統(tǒng)默認(rèn)返回邏輯
      return false
    }
  }
 
  onPageShow(): void { // 頁面進(jìn)入前臺
    console.log('[頁面進(jìn)入前臺]');
    // get
    let value = WebView.WebCookieManager.fetchCookieSync(this.url);
    console.log('[cookie]: ' + value);
    // set
    WebView.WebCookieManager.configCookieSync('url', 'a=1,b=2,c=3');
  }
 
  onPageHide(): void { // 頁面進(jìn)入后臺
    console.log('[頁面進(jìn)入后臺]');
  }
 
  /****** 初始化 init 配置相關(guān) ******/
  async initUserInfo() {
    console.log('[初始化] - 獲取用戶信息')
    userID = await StorageUtils.get('userID') as string
    token = await StorageUtils.get('token') as string
    instituteId = await StorageUtils.get('instituteId') as string
    cellphone = await StorageUtils.get('cellphone') as string
    userInfoData = await StorageUtils.get('UserInfoData') as string
  }
 
 
  webDebugMethod(): void {
    try {
      // 啟用網(wǎng)頁調(diào)試功能
      WebView.WebviewController.setWebDebuggingAccess(true);
    } catch (error) {
      let e: business_error.BusinessError = error as business_error.BusinessError;
      console.log(`[Web] ****** Error Code: ${e.code}, Message: ${e.message}`);
      // this.controller.refresh(); // 頁面異常,刷新
    }
  }
 
  // 白屏情況可能是權(quán)限導(dǎo)致,把權(quán)限全部開啟,例如本地存儲或者 http & https
  build() {
    Column() {
      if (this.navHidden === true) {
        HeaderComponent({ title: this.title })
      }
      if (this.isLoading && !this.url) {
        HProgressHUD({ hudContent: '加載中~~~' })
      }
      if (this.url) {
        Web({ src: this.url, controller: this.controller })
          .width('100%')
          .height('100%')
          .backgroundColor(Color.White)
          .multiWindowAccess(true)
          .javaScriptAccess(true)// 訪問本地資源文件
          .imageAccess(true)// 權(quán)限狀態(tài)開啟
          .onlineImageAccess(true)// 權(quán)限狀態(tài)開啟
          .fileAccess(true)// 權(quán)限狀態(tài)開啟
          .domStorageAccess(true)// 數(shù)據(jù)存儲權(quán)限
          .mediaPlayGestureAccess(true)// 媒體權(quán)限
          .mixedMode(MixedMode.All)// https 加載
          .layoutMode(WebLayoutMode.FIT_CONTENT)// 自適應(yīng)布局
          .verticalScrollBarAccess(true)// 滾動條
          .horizontalScrollBarAccess(false)// 滾動條
          .cacheMode(CacheMode.Default)// 緩存
          .zoomAccess(false)// 禁止手勢縮放
          .geolocationAccess(true)// 定位權(quán)限
          .onConsole((event) => {
            console.log('[交互] - onConsole')
            LogUtils.info(event?.message.getMessage())
            return false
          })
          .onPageBegin(() => { // 頁面加載中
            console.log('[Web] - 頁面加載中:', this.url)
          })
          .onPageEnd(() => {
            console.log('[Web] - 頁面加載完成:', this.url)
            this.isLoading = false
          })
          .onErrorReceive((event) => { // 異常: 無網(wǎng)絡(luò),頁面加載錯誤時
            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) => { // 異常: 網(wǎng)頁加載資源 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) => { // 提示框處理相關(guān)
            AlertDialog.show({
              title: '溫馨提示',
              message: event?.message,
              confirm: {
                value: '確認(rèn)',
                action: () => {
                  event?.result.handleConfirm()
                }
              },
              cancel: () => {
                event?.result.handleCancel()
              }
            })
            return true;
          })
          .onConfirm((event) => { // 提示框處理相關(guān)
            AlertDialog.show({
              title: '溫馨提示',
              message: event?.message,
              confirm: {
                value: '確認(rèn)',
                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
          })
          .javaScriptProxy({
            // web call app, web 組件初始化調(diào)用
            // 對象注入 web
            object: this.WebCallAppMethod,
            name: 'WebCallAppHarmony', // AppCallWeb WebCallAppSsss  WebCallApp
            methodList: ['WebCallApp', 'WebTest', 'WebTestSync', 'WebTestAsync'],
            controller: this.controller
          })
      } else { // 加載異常,請重試
 
      }
    }.width('100%').height('100%')
  }
}
 
/**************************** Web JS Bridge ****************************/
class WebCallAppClass {
  private webView: WebView.WebviewController;
 
  constructor(webView: WebviewController) {
    this.webView = webView
  }
 
  // 方式一: 方法無回調(diào),通過交互協(xié)議 WebCallApp => AppCallWeb
  WebCallApp(value: string) {
    console.log('********* [交互] --- 協(xié)議 - HWebView')
    console.log(value)
 
    // 解析交互協(xié)議
    let params: CommandModel = JSON.parse(value)
    let sn = params.sn
    let command = params.command
    let args: object = params.args
    console.info('********* ', command)
    console.info(sn)
    console.info(command)
    console.info(JSON.stringify(args))
    console.log('********* [交互] - 協(xié)議 *********')
 
    // 用戶信息中添加機(jī)構(gòu)
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    console.log('********* [用戶信息]')
    console.log(JSON.stringify(userInfo))
 
    // 交互處理
    let commandManager = CommandManager.shareInstance()
    let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let jsType = res.type
    let jsonRes = JSON.stringify(res)
    let resData: CommandModel = JSON.parse(jsonRes)
    let argsJson = JSON.stringify(res.args)
    console.log('[交互回調(diào)]:', argsJson)
 
    // App call web 數(shù)據(jù)格式處理
    let appCallWeb = AppCallWeb.shareInstance()
    let callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeNormal, sn, res, null)
    if (jsType == 'Json') {
      callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJson, sn, res, null)
    } else if (jsType == 'Encode') {
      callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJsonEncoded, sn, res, null)
    } else if (jsType == 'Basic') {
      callbacks = appCallWeb.callbackBasic(sn, res, null)
    }
    console.log('********* [callbacks]')
    console.log(callbacks)
    console.log('************')
    this.webView.runJavaScript(`Elf.AppCallWeb(${sn}, ${callbacks})`)
  }
 
  // 方式二: 方法回調(diào),WebCallApp 直接 return
  // WebCallApp(value: string): string { // 直接 return 方式,New_20240703
  //   console.log('[交互] --- WebCallApp - 測試 - HWebView')
  //   console.log(value)
  //
  //   let params: CommandModel = JSON.parse(value)
  //   let command = params.command
  //   let args: object = params.args
  //   if (command === 'CmdOpenUrl') {
  //     let navigation: string = args['navigation']
  //     let name: string = args['name'] ? args['name'] : ''
  //     let staticState: string = args['static']
  //     let url: string = args['url']
  //     try {
  //       router.pushUrl({
  //         url: 'pages/component/HWebView',
  //         params: {
  //           title: name,
  //           navHidden: navigation === '1' ? true : false,
  //           link: url
  //         }
  //       })
  //     } catch (err) {
  //       console.error('[Router] - failed, code is ${(err as BusinessError).code}, message is ${(err as BusinessError).message}')
  //     }
  //     return ''
  //   } else {
  //     // 解析交互協(xié)議
  //     let params: CommandModel = JSON.parse(value)
  //     let sn = params.sn
  //     let command = params.command
  //     let args: object = params.args
  //     console.info(sn)
  //     console.info(command)
  //     console.info(JSON.stringify(args))
  //     // 用戶信息中添加機(jī)構(gòu)
  //     let userInfo: UserInfoModal = JSON.parse(userInfoData)
  //     userInfo.instituteId = instituteId
  //     userInfo.genderCode = genderCode
  //     console.log(JSON.stringify(userInfo))
  //     // 交互處理
  //     let commandManager = CommandManager.shareInstance()
  //     let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
  //     let jsType = res.type
  //     let jsonRes = JSON.stringify(res)
  //     let resData: CommandModel = JSON.parse(jsonRes)
  //     console.log('[交互回調(diào)]:', JSON.stringify(res))
  //     let argsJson = JSON.stringify(res.args)
  //     // App call web 數(shù)據(jù)格式處理
  //     let appCallWeb = AppCallWeb.shareInstance()
  //     let callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeNormal, sn, res, null)
  //     if (jsType == 'Json') {
  //       callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJson, sn, res, null)
  //     } else if (jsType == 'Encode') {
  //       callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJsonEncoded, sn, res, null)
  //     } else if (jsType == 'Basic') {
  //       callbacks = appCallWeb.callbackBasic(sn, res, null)
  //     }
  //     console.log('[callbacks]')
  //     console.log(callbacks)
  //     console.log('************')
  //     return argsJson
  //   }
  // }
 
 
  WebTestAsync = async (value: string): Promise<string> => { // 測試 - 異步
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    let commandManager = CommandManager.shareInstance()
    let res = await commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let argsJson = JSON.stringify(res.args)
    console.log('[WebTestAsync]')
    console.log(argsJson)
    return argsJson
  }
 
  WebTest(value: string): Promise<string> { // 測試 - promise
    let p: Promise<string> = new Promise((resolve, reject) => {
      let userInfo: UserInfoModal = JSON.parse(userInfoData)
      userInfo.instituteId = instituteId
      userInfo.genderCode = genderCode
      let commandManager = CommandManager.shareInstance()
      let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
      let argsJson = JSON.stringify(res.args)
      resolve('[Callback]:' + argsJson)
    })
    return p
  }
}

二.前端

基于 Vue 2 調(diào)試;

1.配置交互消息處理

/* eslint-disable */
import webApp from './index';
import { Toast } from "vant";
 
Date.prototype.format = function(format) {
  var o = {
      "M+": this.getMonth() + 1, //month
      "d+": this.getDate(), //day
      "h+": this.getHours(), //hour
      "m+": this.getMinutes(), //minute
      "s+": this.getSeconds(), //second
      "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
      "S": this.getMilliseconds() //millisecond
  };
  if (/(y+)/.test(format)) {
      format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  }
  for (var k in o) {
      if (new RegExp("(" + k + ")").test(format)) {
          format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
      }
  }
  return format;
};
var AppCallBacks = {},//動態(tài)數(shù)據(jù)流水列表
    AppCommendBackHandlers = [],//APP后退監(jiān)聽事件列表
    AppCommendRefreshHandlers = [],//刷新監(jiān)聽事件列表
    APPCommendReBackHandlers = [],//監(jiān)聽重新回到頁面通知
    AppCommendNetworkHandlers = [],//監(jiān)聽網(wǎng)絡(luò)鏈接狀態(tài)
    AppCommendAppStartingHandlers = [],//監(jiān)聽APP進(jìn)入后臺運行
    AppCommendAppReactivateHandlers = [],//監(jiān)聽APP重新進(jìn)入前臺運行
    AppCommendKeyboardBounceUp = [],
    AppHarmonyHandlers = {}; // 基于 Harmony 適配;
//監(jiān)聽移動端事件
var AppMsgHandlers = {
  //頁面加載完成(TODO plan)
  //"MsgOnReady":function(msg){},
  // 移動端軟鍵盤事件觸發(fā)
  "MsgSoftKeyboardBounceUp": function (data) {
    if (AppCommendKeyboardBounceUp.length > 0) {
      AppCommendKeyboardBounceUp.forEach(item => {
        item.call(window,data);
      });
    }
  },
  //刷新數(shù)據(jù)通知(Realized)
  "MsgRefresh": function (data) {
    //data.type 0 刷新書架和已獲得,1只刷新書架,2,只刷新已獲得
    if (AppCommendRefreshHandlers.length > 0) {
      AppCommendRefreshHandlers.forEach(item => {
        item.call(window,data);
      });
    }
  },
  //后退事件(Realized)
  "MsgGoBack": function (data) {
    webApp.APPCommendBack(data);
  },
  //重新返回到界面(Realized)
  "MsgReBack": function () {
    if (APPCommendReBackHandlers && APPCommendReBackHandlers.length > 0) {
      APPCommendReBackHandlers.forEach(item => {
        item.call(this);
      });
    }
  },
  //網(wǎng)絡(luò)監(jiān)測
  "MsgNetworkState": function (result) {
    if (AppCommendNetworkHandlers && AppCommendNetworkHandlers.length > 0) {
      AppCommendNetworkHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
  //App 后臺運行
  "MsgAppStarting": function (result) {
    if (AppCommendAppStartingHandlers && AppCommendAppStartingHandlers.length > 0) {
      AppCommendAppStartingHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
  //App重新激活
  "MsgAppReactivate": function (result) {
    if (AppCommendAppReactivateHandlers && AppCommendAppReactivateHandlers.length > 0) {
      AppCommendAppReactivateHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
};
 
/*
 *
 * app對接
 * 移動端種植Elf對象
 * window => Elf
 *
 */
(function () {
  if (!window.applicationCache&&typeof(Worker)=='undefined') {
    alert("E001-檢測到您的環(huán)境不支持HTML5,程序終止運行!");//不支持HTML5
    return;
  }
 
  // iOS & Android
  var global = window;//create a pointer to the window root
  if (typeof Elf === 'undefined') {
    global.Elf = {};//create elf root if not existed
  }
  Elf.global = global;//add a pointer to window
 
  if (typeof WebCallApp == 'undefined') {
    global.WebCallApp = {
      WebCallApp: (args) => {},
      // WebCallAppHarmony: (args) => {}
    };
  }
 
  // Harmony
  if (typeof WebCallAppHarmony == 'undefined') {
    global.WebCallAppHarmony = {
      WebCallAppHarmony: (args) => {},
    };
  }
 
  // 測試
  if (typeof WebCallAppTest == 'undefined') {
    global.WebCallAppTest = {
      WebCallAppTest: (args) => {},
    }
  }
 
  // global.WebCallApp.Elf = Elf;
  WebCallApp.global = global;
  WebCallAppHarmony.global = global;
  WebCallAppTest.global = global;
})();
 
 
if (typeof Elf != "undefined") {
  Elf.AppCallWeb = (sn, result) => {
    console.log('[鴻蒙] - 測試');
    console.log('sn:' + sn);
    console.log('res:' + JSON.stringify(result));
 
    if (navigator.userAgent.toLowerCase().indexOf('openharmony') !== -1) { // Harmony
      AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result);
    } else { // iOS & Android
      if (result && typeof result == "string") {
        result = decodeURIComponent(result.replace(/\+/g,'%20'));
        try {
          result = JSON.parse(result);//解決空格變成+的問題
        } catch (error) {
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result);
          return;
        }
        if (result.sn) {
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result.QrCode);
          return;
        }
      }
      if (AppCallBacks[sn]) {
        if (JSON.parse(result.opFlag)) {
          //執(zhí)行對應(yīng)回調(diào)
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,(typeof result.serviceResult == "string") ? JSON.parse(result.serviceResult) : result.serviceResult);
        } else {
          //接口調(diào)用返回失敗信息,統(tǒng)一處理錯誤消息
          Toast(result.errorMessage ? result.errorMessage : "服務(wù)器異常!");
        }
        //調(diào)用完成刪除對象
        delete AppCallBacks[sn];
      } else if (AppMsgHandlers[sn] && typeof AppMsgHandlers[sn] == "function") {
        //處理消息通知
        AppMsgHandlers[sn].call(window,result);
      }
    }
  };
}
export {
  AppCallBacks,
  AppCommendBackHandlers,
  AppCommendAppStartingHandlers,
  APPCommendReBackHandlers,
  AppCommendKeyboardBounceUp,
  AppMsgHandlers,
  AppHarmonyHandlers
}

2.設(shè)置 WebCallApp 方法

/* eslint-disable */
import { Toast } from "vant";
import { AppCallBacks, AppCommendBackHandlers, AppHarmonyHandlers } from './AppMsgHandlers'
 
export default {
  /***********************************************************
   處理App發(fā)送的后退命令
   ***********************************************************/
  APPCommendBack() {
    if (AppCommendBackHandlers.length > 0) {
      if (typeof AppCommendBackHandlers[ AppCommendBackHandlers.length - 1 ] == "function") {
        AppCommendBackHandlers[ AppCommendBackHandlers.length - 1 ].call(window);
      }
    } else {
      if (this.isInApp()) {
        if (!this.isEmptyObject(AppCallBacks)) {
          //Elf.components.toast({text:""});
        } else {
          this.WebCallApp("CmdGoBack");
        }
      } else {
        history.back(-1);
      }
    }
  },
  WebTestAsync(command, args, callback, context) { // 測試
    console.log('WebTestAsync');
    console.log(command);
    console.log(args);
  },
  WebTestSync(command, args, callback, context) { // 測試
    console.log('WebTestSync');
    console.log(command);
    console.log(args);
  },
  WebCallApp(command, args, callback, context) {
    /**
     * 交互
     *
     * 協(xié)議:command
     * 入?yún)?args
     * 回調(diào):callback
     * 參數(shù):context
     * */
    if (typeof Elf.AppCallWeb != "undefined") {
      context = context || window;//默認(rèn)為window對象
      args = args || {};
      let sn = null;
      //IOS調(diào)用相機(jī)--sn特殊處理
      if (command == "callCamera") {
        sn = "examCamera";
      } else {
        sn = this.getSerialNumber();//請求App統(tǒng)一加水單號
      }
      console.log('first:', sn);
      let params = {
        args : args,
        command : command,
        sn : sn,
      };
      //綁定回調(diào)函數(shù)
      if (callback) {
        AppCallBacks[ sn ] = {
          callback : callback,
          context : context
        };
      }
      if (window.webkit && window.webkit.messageHandlers) { // iOS
        params.sn = sn;
        window.webkit.messageHandlers[ "WebCallApp" ].postMessage(JSON.stringify(params));
      } else if (Elf.WebCallApp) { // Android
        params.sn = sn;
        Elf.WebCallApp(JSON.stringify(params));
      } else if (this.isInCef()) { // PC
        params.sn = sn;
        Elf.WebCallCef(JSON.stringify(params));
      } else if (this.isInHarmonyOS()) { // HarmonyOS Next
        // 方式一: WebCallApp => AppCallWeb 交互通信方式
        params.sn = sn;
        WebCallAppHarmony[ 'WebCallApp' ](JSON.stringify(params));
        // 方式三: callback 延遲回調(diào)
        // console.log('sn - 0:', sn);
        // let timer = setTimeout(() => {
        //   console.log('sn - 1:', sn);
        //   if (callback) {
        //     console.log('sn - 2:', sn);
        //     callback(AppHarmonyHandlers.result);
        //     clearTimeout(timer);
        //   }
        // }, 500);
 
        // let result = WebCallApp[ 'WebCallApp' ](JSON.stringify(params)); // old
        // 方式二: WebCallApp 直接交互
        // let result = WebCallAppHarmony[ 'WebCallApp' ](JSON.stringify(params)); // 適配考試
        // console.log(result);
        // callback
        // if (result && typeof result == 'string') {
        //   try {
        //     result = JSON.parse(result);
        //   } catch (error) {
        //     console.log(JSON.parse(result));
        //     AppCallBacks[ sn ].callback.call(AppCallBacks[ sn ].context, result);
        //     return
        //   }
        //   if (result.sn) {
        //     AppCallBacks[ sn ].callback.call(AppCallBacks[ sn ].context, result);
        //     return;
        //   }
        // }
      } else {
        //直連接口服務(wù)器
        // if (Config.services[command]) {
        //   args.token = User.token;
        //   if (command == "UserLogin") {
        //     args.token = -1;
        //   }
        //   var request = {
        //     terminalType: "A",
        //     deviceType: "1",
        //     serviceModule: Config.services[command].sm,
        //     serviceNumber: Config.services[command].sn,
        //     token: command == "UserLogin" ? "-1" : User.token,
        //     args: args
        //   };
        //   options.data = request;
        //   options.method = 'post';
        //   options.url = process.env.root;
        //   options.dataType = 'json';
        //   axios(options).then(res => {
        //     Elf.AppCallWeb(sn,data);
        //     Toast.clear();
        //   }).catch(
        //     error => {
        //       var errorData = { opFlag: false,errorMessage: "總線錯誤" };
        //       Elf.AppCallWeb(sn,JSON.stringify(errorData),"JSON");
        //     }
        //   );
        // }
      }
    }
  },
  AppCommendTostInfo(result) {
    if (result.msg) {
      Toast(result.msg);
    }
  },
  /***********************************************************
   App Call Web 入口方法,
   All of app to web use that
   ***********************************************************/
  isInApp() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return !!((window.webkit && window.webkit.messageHandlers) || typeof Elf.WebCallApp == "function" || typeof Elf.WebCallCef == "function" || typeof Elf.getTestDataAsync == "function");
      // || typeof Elf.WebCallAppHarmony == "function"
    }
  },
  isInIOS() {
    return window.webkit && window.webkit.messageHandlers;
  },
  isInAndroid() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return typeof Elf.WebCallApp == "function";
    }
  },
  isInHarmonyOS() {
    if (navigator.userAgent.toLowerCase().indexOf('openharmony') !== -1) {
      return true;
    } else {
      return false;
    }
  },
  isInCef() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return typeof Elf.WebCallCef == "function";
    }
  },
  isEmptyObject(obj) {
    let name;
    for (name in
      obj) {
      return false;
    }
    return true;
  },
  UUID(len, radix) {
    var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
    var uuid = [],
      i;
    radix = radix || chars.length;
    if (len) {
      for (i = 0;
        i < len;
        i++) {
        uuid[ i ] = chars[ 0 | Math.random() * radix ];
      }
    } else {
      var r;
      uuid[ 8 ] = uuid[ 13 ] = uuid[ 18 ] = uuid[ 23 ] = '-';
      uuid[ 14 ] = '4';
      for (i = 0;
        i < 36;
        i++) {
        if (!uuid[ i ]) {
          r = 0 | Math.random() * 16;
          uuid[ i ] = chars[ (i == 19) ? (r & 0x3) | 0x8 : r ];
        }
      }
    }
    return uuid.join('');
  },
  getSerialNumber() {
    // var uuid = this.UUID(3, 8);
    // return new Date().format("yyyyMMddhhmmssS") + uuid; // after
    return Math.random().toString(); // new
  },
}

3.全局設(shè)置

跟目錄 main.js 中配置為全局

import webApp from '../src/expand/webApp/index';
import { AppCommendBackHandlers } from "./expand/webApp/AppMsgHandlers";
 
Vue.prototype.webApp = webApp;
Vue.prototype.WebBack = AppCommendBackHandlers;

4.交互調(diào)用

this.webApp.WebCallApp('GetAPPDetail', {'abc': '123'}, (res) => { // 測試: 鴻蒙 jsbridge 交互
  console.log('[交互] - 回調(diào)');
  console.log(res);
});

以上便是此次分享的全部內(nèi)容,希望能對大家有所幫助!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容