WKWebViewJavascriptBridge 源码解读

使用方法

  1. 原生
class ViewController: UIViewController {
    let webView = ...
    var bridge: WKWebviewJavascriptBridge!
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        bridge = WKWebViewJavascriptBridge(webview: webView)
        bridge.register(handlerName: "xxx") { params, callback in
            
        }
    }
    ...
    func click() {
        bridge.call(handlerName: "xxxx") { params, callback in
        
        }
    }
}
复制代码
  1. vue 改造
function setupWebViewJavascriptBridge(callback) {
  if (window.WebViewJavascriptBridge) {
    return callback(window.WebViewJavascriptBridge);
  }
  if (window.WVJBCallbacks) {
    return window.WVJBCallbacks.push(callback);
  }
  window.WVJBCallbacks = [callback];
  let WVJBIframe = document.createElement("iframe");
  WVJBIframe.style.display = "none";
  WVJBIframe.src = "https://__bridge_loaded__";
  document.documentElement.appendChild(WVJBIframe);
  setTimeout(() => {
    document.documentElement.removeChild(WVJBIframe);
  }, 0);
}
const callhandler = (name, data, callback) => {
  setupWebViewJavascriptBridge(function(bridge) {
    bridge.callHandler(name, data, callback);
  });
};
const registerhandler = (name, callback) => {
  setupWebViewJavascriptBridge(function(bridge) {
    bridge.registerHandler(name, function(data, responseCallback) {
      callback(data, responseCallback);
    });
  });
};
export { registerhandler, callhandler };
复制代码
  1. vue 中使用
import { callhandler, registerhandler } from './bridge';
...
mounted() {
    registerhandler(<name>, () => {
    });
},
methods: {
    callMobile() {
        callhandler(<name>, params, (result) => {
        });
    }
}

复制代码

或者 在 main.js

import Bridge from './bridge.js'
Vue.prototype.$bridge = Bridge
this.$bridge.callhandler(...)
this.$bridge.register(...)
复制代码

原生通信

当我们不借助这个三方库,我们原生应该怎样直接交互呢?

js 调用原生

  1. js
// 这里需要注意如果 messageBody 有且必须有一个参数,如果不需要就传 null
// <name> 就是和移动端协商的函数名
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
复制代码
  1. 原生
class ViewController: UIViewController {
    override func viewdidLoad() {
        super.viewDidLoad()
        // 这里有个细节就是需要注意循环引用的问题
        webView?.configuration.userContentController.add(LeakAvoider(delegate: self), name: <name>)
    }
    // 这里一定要记得释放
    deinit {
        webView?.configuration.userContentController.removeScriptMessageHandler(forName: <name>)
    }
}
extension ViewController: WKScriptMessageHandler {
    public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == <name> {
        // doSomething(message.body)
    }
}
复制代码

原生调用 js

  1. 原生
webview.evaluateJavaScript(<name>, completionHandler: completion)
复制代码
  1. js
window.<name> = function(param) {
    // doSomething()
}
复制代码

原理

初始化

  1. js 调用window.webkit.messageHandlers.iOS_Native_InjectJavascript.postMessage(null)
  2. iOS 执行 webView.evaluateJavascript(javascript: WKWebViewJavascriptBridgeJS, ...)

这样在 js 和原生都会有一个 messageHandlersresponseCallbacks,后续可以根据字符串去匹配对应的函数执行,并且会有一个自增的 uniqeID 记录对应的回调。

猜你喜欢

转载自juejin.im/post/7016989240382717988