Vue与原生开发间的通信jsBridge

4,374 阅读1分钟

一、vue插件写法

1. utils/jsBridge/Bridge.js //封装了与原生注册通讯的函数
2. utils/jsBridge/BridgeCmd.js //每个Api的名字,与原生开发人员约定好
3. utils/jsBridge/BridgeRegisterHandle.js //封装桥能力的函数,暴露出去

二、Bridge.js文件:实现与原生通讯的代码段,插件的注册,已经将方法挂在到vue原型上,方法集中在此处理。

import { isAndroidModel, isIosModel } from '@/utils/model.js';//该文件用来判断平台环境iOS,Android
const JsBridgePlugin = {
  install(Vue, option) {
    if (isAndroidModel || isIosModel) {
      Vue.prototype.$jsBridge = {
        callHandler: function () {},
        registerHandler: function () {}
      }
    }
    if (isAndroidModel) {//Android
      androidBridge(function (bridge) {
        bridge.init(function (message, responseCallback) {
          console.log('JS got a message', message)
          const data = {
            'Javascript Responds': 'Wee!'
          }
          responseCallback(data)
        })
        Vue.prototype.$jsBridge = bridge
        option.ready()
      })
    }
    if (isIosModel) {//iOS
      iosBridge(function (bridge) {
        const {
          callHandler,
          registerHandler
        } = bridge
        Vue.prototype.$jsBridge = bridge
        Object.assign(Vue.prototype.$jsBridge, {
          callHandler() {
            callHandler(...arguments);
          },
          registerHandler() {
            registerHandler(...arguments)
          }
        })
        option.ready()
      })
    }
  }
}

// Android调用原生的方法封装:
// (前端js与原生Native通信方法外层包裹的固定代码)
function androidBridge(callback) {
  if (window.WebViewJavascriptBridge) {
    return callback(window.WebViewJavascriptBridge)
  }
  if (window.WVJBCallbacks) {
    return window.WVJBCallbacks.push(callback)
  }
  window.WVJBCallbacks = [callback]
  var WVJBIframe = document.createElement('iframe')
  WVJBIframe.style.display = 'none'
  WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'
  document.documentElement.appendChild(WVJBIframe)
  let timerRemoveIframe = setTimeout(function () {
    document.documentElement.removeChild(WVJBIframe)
    clearTimeout(timerRemoveIframe)
    timerRemoveIframe = null
  }, 0)
  if (window.WebViewJavascriptBridge) {
    return callback(window.WebViewJavascriptBridge)
  } else {
    document.addEventListener('WebViewJavascriptBridgeReady', function () {
      callback(window.WebViewJavascriptBridge)
    }, false)
  }
}
// iOS调用原生的方法封装:
function iosBridge(callback) {
  if (window.WebViewJavascriptBridge) {
    return callback(window.WebViewJavascriptBridge)
  }
  if (window.WVJBCallbacks) {
    return window.WVJBCallbacks.push(callback)
  }
  window.WVJBCallbacks = [callback]
  var WVJBIframe = document.createElement('iframe')
  WVJBIframe.style.display = 'none'
  WVJBIframe.src = 'https://__bridge_loaded__'
  document.documentElement.appendChild(WVJBIframe)
  let timerRemoveIframe = setTimeout(function () {
    document.documentElement.removeChild(WVJBIframe)
    clearTimeout(timerRemoveIframe)
    timerRemoveIframe = null
  }, 0)
}
export default JsBridgePlugin

三、BridgeCmd.js: 调用原生能力的Api名字(与原生约定好)。

// callHandler:H5调用原生能力
export const getAppInfo = 'getAppInfo' // 获取平台信息
export const downloadZip = 'downloadZip'// 下载zip
// registerHandler:原生调用H5
export const downloadZipHandle = 'downloadZipHandle'// 监听下载进度

四、BridgeRegisterHandle.js:原生返回给vue的回调函数的封装。

import Vue from 'vue';
import { downloadZip, getAppInfo, downloadZipHandle } from './BridgeCmd.js';
export const jsBridgeGetAppInfo = ({ params = {}, complete = () => {} } = {}) => {
  const jsBridge = Vue.prototype.$jsBridge
  const { callHandler } = jsBridge
  callHandler( getAppInfo, params, res => complete(res) );
}
export const jsBridgeDownloadZip = ({ params = { url: '' }, complete = () => {} } = {}) => {
  const jsBridge = Vue.prototype.$jsBridge
  const { callHandler } = jsBridge
  callHandler( downloadZip, params, res => complete(res) );
}
export const jsBridgeDownloadZipHandle = ({ complete = () => {} } = {}) => {
  const jsBridge = Vue.prototype.$jsBridge
  const { registerHandler } = jsBridge
  registerHandler( downloadZipHandle, res => complete(res) );
}

五、vue 项目 main.js文件

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 异步懒加载引入Bridge,注意:import函数是异步引入,若是刚打开项目时候,需要马上调用某个桥能力,则需要重新考虑引入时机。
import( /* webpackChunkName:"Bridge" */ '@/utils/jsBridge/Bridge.js').then((module) => {
  const Bridge = module.default
  Vue.use(Bridge, {
    ready() { console.log('桥链接初始化完毕', '<<<--- ') }
  });
});
new Vue({
  render: h => h(App),
}).$mount('#app')

六、页面调用

<template>
  <div>
    <h1>vue与原生混合开发--jsBridge</h1>
  </div>
</template>
<script>
import { jsBridgeDownloadZip,jsBridgeDownloadZipHandle} from '@/utils/jsBridge/BridgeRegisterHandle.js'
export default {
  name: "App",
  mounted() {
    //H5调用原生方式:::
    jsBridgeDownloadZip({
        params: {
          url: `https://console.warpiot.com/iot-app/appDownload/test_v1/dist.zip?t=${Date.now()}`
        },
        complete: (res) => { console.log(res, '<<<--- jsBridgeDownloadZip res') }
      });
   }
   //原生调用H5,类似监听,通知给H5说下载进度如何了
    jsBridgeDownloadZipHandle({
        complete: (res) => {
          console.log(res, '<<<--- 下载进度')
          const { code } = res //code: 1000 , 1001 , 1002 , 1003 , 1004 , 1005
          if (code === 1003) {
            console.log('下载结束', '<<<--- ')
            window.location.reload()
          }
        }
      });
};
</script>
<style lang="scss" scope>
</style>