WebView与JS的通讯机制

1,562 阅读3分钟

前言

WebView:由于引入WebKit即成为"应用里的浏览器",可以支持对网页解析,支持浏览器加载网页

JS(JavaScript):开发Web页面的脚本语言,是前端的语言

WebViewh5前端页面之间需要存在一种通讯机制来使WebView上的h5可以使用应用的原生能力,即产生JSBridge

JSBridge :Native代码与JS代码的通信桥梁,JS 代码调用”桥“的机制来使用原生的能力,而原生代码通过“桥”来给JS代码发事件

JS桥的使用

JavaScript调用Native的方式

  • 注入API

对于UIWebView

import Foundation
import Foundation
import UIKit
import WebKit
import JavaScriptCore
//如果一个协议遵守了JSExport,那么该协议的方法会对JS开放,允许JS直接调用该方法
protocol JSDelegate:JSExport{
  //原生方法定义
    func checkExecuteJSBridge(str:NSString)
}
class JSModel:NSObject,JSDelegate{
    var jsContext:JSContext!
    weak var controller: UIWebViewController?
    //对应的某原生方法
    func checkExecuteJSBridge(str:NSString){
        //TODO
        print(str)
    }
}
class UIWebViewController: UIViewController,UIWebViewDelegate{
    var webView: UIWebView!
    var jscontext: JSContext{
        var context: JSContext
        //向 JavaScript 的 Context(window)中注入对象或者方法(self.jscontext),获取JS代码的执行环境/上下文/作用域
        context = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext
        return context
    }

    override func loadView() {
        webView = UIWebView(frame: .zero)
        webView.delegate = self
        view = webView
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let myURL = URL(string: "https://www.baidu.com")
        let myRequest = URLRequest(url: myURL!)
        webView.loadRequest(myRequest)
        
    }
    func webViewDidFinishLoad(_ webView: UIWebView) {
        let model = JSModel()
        model.controller = self
        model.jsContext = self.jscontext
       //在context注册JSDelegate对象,使JS可以通过UIWebViewBridge方法来调用原生的方法checkExecuteJSBridge
        self.jscontext.setValue(model, forKey: "UIWebViewBridge")
    }
}

前端通过JS桥调用Native功能

if(window.UIWebViewBridge)return window.UIWebViewBridge(e) 

对于WKWebView

import Foundation
import UIKit
import WebKit
import JavaScriptCore
class WKWebViewController: UIViewController, WKUIDelegate,WKNavigationDelegate, WKScriptMessageHandler{
    //监听JS调用原生方法的代理方法
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        //JS如果调用了WKWebViewBridge方法,执行对应的原生操作
        if message.name == "WKWebViewBridge" {
            self.checkExecuteJSBridge(str: message.body as! NSString)
        }
    }
    
    var jscontext: JSContext!
    var webView: WKWebView!
    override func loadView() {
        //网页配置类
        let webConfiguration = WKWebViewConfiguration()
        
        //WKUserContentController控制JS与原生的交互,是向js方法注入和移除注入方法的类
        let usercontentController = WKUserContentController()
        //向JS注入了WKWebViewBridge方法
        usercontentController.add(self, name: "WKWebViewBridge")
        webConfiguration.userContentController = usercontentController
        
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        webView.allowsBackForwardNavigationGestures = true

        
        view = webView
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let myURL = URL(string: "https://www.baidu.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)

    }
    //原生方法
    func checkExecuteJSBridge(str:NSString){
        // TODO
        print(str)
    }
}

前端通过JS桥调用Native功能

if(window.webkit&&window.webkit.messageHandlers&&window.webkit.messageHandlers.WKWebViewBridge)
  return window.webkit.messageHandlers.WKWebViewBridge.postMessage({str}),f
  • 拦截URL SCHEME

URL SCHEME:类似于url的链接,是为了方便app直接互相调用设计的,形式和普通的 url 近似,主要区别是 protocol 和 host 一般是自定义的

对于UIWebView

    //webView准备加载前的一个代理方法
    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
        var requestString = request.url?.absoluteString
        if((requestString?.hasPrefix("special")) != nil){
            //JS以前缀为“special”的特殊URL加载来调用原生的对应方法
            //TODO
            //停止正常http网络协议加载URL
            return false
        }
        //正常以http网络协议加载URL
        return true
    }

对于WKWebView

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        var requestString = navigationAction.request.url?.absoluteString
        if((requestString?.hasPrefix("special")) != nil){
            //JS以前缀为“special”的特殊URL加载来调用原生的对应方法
            //TODO
            //停止正常http网络协议加载URL
            decisionHandler(.cancel)
        }
        //正常以http网络协议加载URL
        decisionHandler(.allow)
    }

Native 调用 JavaScript 的方式

Native 调用 JavaScript 较为简单,直接执行拼接好的 JavaScript 代码即可
对于UIWebVIew

//这里的JavaScript 代码 是”refresh“
self.webView.evaluateJavaScript("refresh", completionHandler: nil)

对于WKWebView

//这里的JavaScript 代码 是”refresh“
self.webView.stringByEvaluatingJavaScript(from: "refresh")