iOS WebViewJavascriptBridge源码解析

9,339 阅读10分钟

一. 概述

做客户端开发免不了要与WebView打交道,特别是对于Hybrid App,在H5所占比重越来越大的背景下,一套好的WebView 与原生交互的API显得尤为重要,当然目前两端都有比较成熟的三方库进行支持。比如Android端的JsBridge,iOS端的WebViewJavascriptBridge,但是对于其内部原理笔者一直一知半解,导致有时面对问题无从下手,最后决心分析WebViewJavascriptBridge的内部实现原理,一是提升自己的源码阅读水平,其次也希望对以后的工作有所帮助。

二. 基本原理

pic 下载WebViewJavascriptBridge的源码后可以看到其文件并不多,分别对几个文件做简单的介绍,后面详细分析其源码

  • WebViewJavascriptBridge_JS: JS桥接文件,通过它实现JS环境的初始化,里面就一个C函数,返回的是JS方法。原生调用的JS方法与对应的方法回调都需要先在这里面进行注册。
  • WKWebViewJavascriptBridgeWebViewJavascriptBridge: WKWebViewUIWebView对应的桥接文件。JS调用的原生方法与对应的方法回调都需要先在这里面进行注册。
  • WebViewJavascriptBridgeBase: 桥接基础文件。通过他实现对原生环境的初始化,以及对方法存储容器的初始化,当然还有对WebViewJavascriptBridge_JS里面JS方法的调用。

三. 源码解析

大体了解了上面几个类的作用,我们通过源码来分析其内部的实现逻辑。我们就以WebViewJavascriptBridgeDemo为例。

1. JS调用OC方法

(1) OC环境初始化与方法注册

如何实现JS调用OC方法呢,首先要对当前OC环境进行初始化

// ExampleWKWebViewController
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
    [_bridge setWebViewDelegate:self];
    
    [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"testObjcCallback called: %@", data);
        responseCallback(@"Response from testObjcCallback");
    }];

....

// WKWebViewJavascriptBridge
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
    WKWebViewJavascriptBridge* bridge = [[self alloc] init];
    [bridge _setupInstance:webView];
    [bridge reset];
    return bridge;
}

....
// WKWebViewJavascriptBridge
- (void) _setupInstance:(WKWebView*)webView {
    _webView = webView;
    _webView.navigationDelegate = self;
    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}
  • [WebViewJavascriptBridge bridgeForWebView:webView];: 看这个方法的调用栈,可以清晰的看到其作用是初始化 WKWebViewJavascriptBridge,进而实例化其对应的WebViewJavascriptBridgeBase,还有绑定各自的代理,最终实现初始化OC调用环境的目的。

  • - (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; : 如果要实现JS调用原生方法的目的,那么必须对原生方法进行注册,这个就是对应的注册方法。我们来看他内部做了什么:

- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}

很简单,只不过把当前的Block保存进了messageHandlers这个字典中,以便等JS端调用时,通过方法名称来找到其对应的实现。

(2) JS环境初始化与方法触发

OC环境初始化与方法注册完成后,我们来下JS环境的初始化 Demo中通过- (void)loadExamplePage:(WKWebView*)webView 方法加载网页到当前的webView,来看下ExampleApp.html中的核心方法:

function setupWebViewJavascriptBridge(callback) {
      if (window.WebViewJavascriptBridge) { return callback(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);
      setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
  }

  setupWebViewJavascriptBridge(function(bridge) {
  var uniqueId = 1
  function log(message, data) {
    var log = document.getElementById('log')
    var el = document.createElement('div')
    el.className = 'logLine'
    el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
    if (log.children.length) { log.insertBefore(el, log.children[0]) }
    else { log.appendChild(el) }
  }

  bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
    log('ObjC called testJavascriptHandler with', data)
    var responseData = { 'Javascript Says':'Right back atcha!' }
    log('JS responding with', responseData)
    responseCallback(responseData)
  })

  document.body.appendChild(document.createElement('br'))

  var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
  callbackButton.innerHTML = 'Fire testObjcCallback'
  callbackButton.onclick = function(e) {
    e.preventDefault()
    log('JS calling handler "testObjcCallback"')
    bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
      log('JS got response', response)
    })
  }
})
  • setupWebViewJavascriptBridge(callback)是核心方法,webView加载html后会首先调用这个方法。这个方法需要一个参数callback,也是一个函数。我们来看这个方法:
function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(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);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

第一次加载网页时 window.WebViewJavascriptBridgewindow.WVJBCallbacks都是false,把window.WVJBCallbacks赋值为包含callback的数组,此时callback为一个函数,就是后面的function(bridge) ....,接下来创建WVJBIframe,你可以把它理解为一个空白页面,创建它的目的是设置src = 'https://__bridge_loaded__';,

注意这个src属性很关键,当我们设置一个网页的src属性时,这个链接会被我们OC端的webView所捕获,从而调用webView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler,

后面两句代码的的意思是加载当前空白页,以便触发OC的代理方法,然后立马移除。

  • 接下来我们去WKWebViewJavascriptBridge中看 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 这个代理方法拦截到请求后做了什么。
NSURL *url = navigationAction.request.URL;
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;

if ([_base isWebViewJavascriptBridgeURL:url]) {
    if ([_base isBridgeLoadedURL:url]) {
        [_base injectJavascriptFile];
    } else if ([_base isQueueMessageURL:url]) {
        [self WKFlushMessageQueue];
    } else {
        [_base logUnkownMessage:url];
    }
    decisionHandler(WKNavigationActionPolicyCancel);
    return;
}

首先判断当前的URL是否是__wvjb_queue_message__或者__bridge_loaded__,刚才触发的URL是 __bridge_loaded__ 会调用WebViewJavascriptBridgeBase- (void)injectJavascriptFile方法。

- (void)injectJavascriptFile {
    // 获取JS字符串
    NSString *js = WebViewJavascriptBridge_js();
    [self _evaluateJavascript:js];
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}

....

- (void) _evaluateJavascript:(NSString *)javascriptCommand {
    [self.delegate _evaluateJavascript:javascriptCommand];
}

....

- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
    [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    return NULL;
}

通过以上方法调用可以看到,最后是把WebViewJavascriptBridge_js(); JS方法字符串,通过方法 [_webView evaluateJavaScript:javascriptCommand completionHandler:nil] 注入到了webView中并且执行。从而达到初始化javascript环境的brige的作用。

  • WebViewJavascriptBridge_js()方法解析
window.WebViewJavascriptBridge = {
  registerHandler: registerHandler,
  callHandler: callHandler,
  disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
  _fetchQueue: _fetchQueue,
  _handleMessageFromObjC: _handleMessageFromObjC
};

var messagingIframe;
  // 要发送给原生的消息列表
var sendMessageQueue = [];
  // 存储注册在bridge的JS方法
var messageHandlers = {};
// 要跳转的URL
var CUSTOM_PROTOCOL_SCHEME = 'https';
var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
//JS方法回调
var responseCallbacks = {};
var uniqueId = 1;
var dispatchMessagesWithTimeoutSafety = true;
  // OC调用的JS方法需要用它来进行注册
function registerHandler(handlerName, handler) {
  messageHandlers[handlerName] = handler;
}
//JS调用OC的方法入口
function callHandler(handlerName, data, responseCallback) {
  if (arguments.length == 2 && typeof data == 'function') {
    responseCallback = data;
    data = null;
  }
  _doSend({ handlerName:handlerName, data:data }, responseCallback);
}
function disableJavscriptAlertBoxSafetyTimeout() {
  dispatchMessagesWithTimeoutSafety = false;
}
// 要发送消息给原生了
function _doSend(message, responseCallback) {
  if (responseCallback) {
    var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
    responseCallbacks[callbackId] = responseCallback;
    message['callbackId'] = callbackId;
  }
  sendMessageQueue.push(message);
      //触发webView 代理,解析JS 的message
  messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
  // 把消息转成json字符串
function _fetchQueue() {
  var messageQueueString = JSON.stringify(sendMessageQueue);
  sendMessageQueue = [];
  return messageQueueString;
}

function _dispatchMessageFromObjC(messageJSON) {
  ....
}
// 原生会调用他,JS用它来达到消息分发
function _handleMessageFromObjC(messageJSON) {
      _dispatchMessageFromObjC(messageJSON);
}

messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);

setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
  var callbacks = window.WVJBCallbacks;
  delete window.WVJBCallbacks;
  for (var i=0; i<callbacks.length; i++) {
    callbacks[i](WebViewJavascriptBridge);
  }
}

我截选了一些关键代码,首先整个WebViewJavascriptBridge_js是一个JS方法的执行,首先创建了JS端的WebViewJavascriptBridge并赋值给了window,我们来看这个对象的构成:

window.WebViewJavascriptBridge = {
  registerHandler: registerHandler,
  callHandler: callHandler,
  disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
  _fetchQueue: _fetchQueue,
  _handleMessageFromObjC: _handleMessageFromObjC
};
  • registerHandler:直接对应下面的registerHandler(handlerName, handler) 方法,通过它我们把能被OC调用的JS方法进行注册,看它的实现也是比较简单的
function registerHandler(handlerName, handler) {
	messageHandlers[handlerName] = handler;
}

把JS的方法实现以方法名handleName保存在messageHandlers中。

  • callHandler: 对应下面callHandler(handlerName, data, responseCallback)方法,通过它我们可以直接发起对OC方法的调用,具体调用逻辑我们在下面进行分析。
  • disableJavscriptAlertBoxSafetyTimeout:回调是否超时开关,默认为false
  • _fetchQueue: 把javascript环境的方法序列化成JSON字符串,并返回给OC端
  • _handleMessageFromObjC:处理OC发给javascript环境的方法,_dispatchMessageFromObjC(messageJSON)这个方法的参数就是OC调用JS的message信息,这个方法对messageJSON进行解析处理,进而调用相应的JS方法。
messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);

这里的src就是https://wvjb_queue_message,这段代码的意思是把javascript要发送给OC的消息立即发送出去。

setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
  var callbacks = window.WVJBCallbacks;
  delete window.WVJBCallbacks;
  for (var i=0; i<callbacks.length; i++) {
    callbacks[i](WebViewJavascriptBridge);
  }
}

WebViewJavascriptBridge_js的最后是上面的代码,它会调用ExampleApp.html中的callBack方法,也就是它

setupWebViewJavascriptBridge(function(bridge) {
 ....
})

继而完成对这个JS环境的初始化与ExampleApp.html的加载。

(3) JS调用OC方法流程

  • 点击JS按钮触发下面的方法
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
	log('JS got response', response)
})

传递方法名testObjcCallback,消息参数{'foo': 'bar'},以及OC回调JS的方法function(response) {log('JS got response', response)}),

  • 调用WebViewJavascriptBridge_js
function callHandler(handlerName, data, responseCallback) {
  if (arguments.length == 2 && typeof data == 'function') {
    responseCallback = data;
    data = null;
  }
  _doSend({ handlerName:handlerName, data:data }, responseCallback);
}

....

function _doSend(message, responseCallback) {
  if (responseCallback) {
    var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
    responseCallbacks[callbackId] = responseCallback;
    message['callbackId'] = callbackId;
  }
  sendMessageQueue.push(message);
  messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}

可以看到核心方法是_doSend(),入参message是调用OC的方法名与参数,responseCallback是OC回调JS的方法,接下来将这个回调方法保存在responseCallbacks中,key值是callbackId,消息对象message也添加一个callbackId,最后设置messagingIframe的src属性,从而被ebView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler拦截。

  • 在上面的代理方法中,拦截到的URL为__wvjb_queue_message__,所以调用方法:
- (void)WKFlushMessageQueue {
    [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
        if (error != nil) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
        }
        [_base flushMessageQueue:result];
    }];
}

....

- (NSString *)webViewJavascriptFetchQueyCommand {
    return @"WebViewJavascriptBridge._fetchQueue();";
}

WebView触发JS的WebViewJavascriptBridge._fetchQueue(),

function _fetchQueue() {
	var messageQueueString = JSON.stringify(sendMessageQueue);
	sendMessageQueue = [];
	return messageQueueString;
}

这个方法里面会将sendMessageQueue换成json字符串,然后返回给OC环境,触发[_base flushMessageQueue:result];

- (void)flushMessageQueue:(NSString *)messageQueueString{
    if (messageQueueString == nil || messageQueueString.length == 0) {
        NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
        return;
    }
    // JS传递过来的json字符串,我们进行反序列化 得到message数组
    NSLog(@"messageQueueString===%@",messageQueueString);
    id messages = [self _deserializeMessageJSON:messageQueueString];

    for (WVJBMessage* message in messages) {
        if (![message isKindOfClass:[WVJBMessage class]]) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
            continue;
        }
        [self _log:@"RCVD" json:message];
        
        NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]);
            [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) { // 有回调
                responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }
                    
                    WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };
            } else {
                responseCallback = ^(id ignoreResponseData) {
                    // Do nothing
                };
            }
            WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];

            if (!handler) {
                NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }
            //向下传递参数,并且触发block回调()
            handler(message[@"data"], responseCallback);
        }
    }
}

这个方法就是OC端处理JS的核心方法了,将messageQueueString反序列化,得到消息数组

(
       {
       callbackId = "cb_1_1639553291614";
       data =         {
           foo = bar;
       };
       handlerName = testObjcCallback;
   }
)

callbackId,表明有消息回调,生成responseCallbackBlock,这个Block里面将接收的参数与callbackId打包一并发送给JS环境,并调用JS环境的WebViewJavascriptBridge._handleMessageFromObjC(messageJSON);方法将messageJSON进行解析。这里只是Block的实现,并没调用这个Block,调用在下面

WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];

if (!handler) {
    NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
    continue;
}
//向下传递参数,并且触发block回调()
handler(message[@"data"], responseCallback);

根据handlerName找到在messageHandlers保存的方法实现,handler(message[@"data"], responseCallback);进行真正的调用,在OC的注册方法中,调用responseCallback(@"Response from testObjcCallback"); 向JS环境发送回调并传递参数。

  • JS环境通过这个_handleMessageFromObjC(messageJSON)方法得到messageJSON,并对其解析。

    function _dispatchMessageFromObjC(messageJSON) {
      if (dispatchMessagesWithTimeoutSafety) {
        setTimeout(_doDispatchMessageFromObjC);
      } else {
         _doDispatchMessageFromObjC();
      }
    
      function _doDispatchMessageFromObjC() {
      //转换为对象
        var message = JSON.parse(messageJSON);
        var messageHandler;
        var responseCallback;
        // 这个responseId就是JS调用OC方法保存的callbackId
        if (message.responseId) {
          responseCallback = responseCallbacks[message.responseId];
          if (!responseCallback) {
            return;
          }
          responseCallback(message.responseData);
          delete responseCallbacks[message.responseId];
        } else {
          if (message.callbackId) {
            var callbackResponseId = message.callbackId;
            responseCallback = function(responseData) {
              _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
            };
          }
    
          var handler = messageHandlers[message.handlerName];
          if (!handler) {
            console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
          } else {
            handler(message.data, responseCallback);
          }
        }
      }
    }
    

    先将字符串转化为JSON对象,根据responseId(这个responseId就是JS调用OC方法保存的callbackId),找到对应的方法实现,进行调用

    function(response) {
      log('JS got response', response)
    }
    

    到此就完成了JS调用OC,并且OC回调JS并传递参数的全部过程。

2. OC调用JS方法

跟上面类似再来看下OC主动调用JS方法的实现

[_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];
....
 - (void)callHandler:(NSString *)handlerName data:(id)data {
    [self callHandler:handlerName data:data responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}

....
  
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    NSMutableDictionary* message = [NSMutableDictionary dictionary];
    
    if (data) {
        message[@"data"] = data;
    }
    
    if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }
    
    if (handlerName) {
        message[@"handlerName"] = handlerName;
    }
    [self _queueMessage:message];
}
....
  
- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
    
    NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    NSLog(@"javascriptCommand==%@",javascriptCommand);
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];

    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}

可以看到,跟JS调用OC方法的原理类似,将OC调用JS的方法名与参数封装进message对象,如果有回调函数,将回调函数通过responseCallbacks保存,并生成callbackId,将整个message打包发送给JS环境的WebViewJavascriptBridge._handleMessageFromObjC(messageJSON); 进行解析,解析流程上面介绍过了,这里不再赘述。

四. 总结

通过上面流程分析,整个WebViewJavascriptBridge 内部的实现原理就比较清晰了。

  • JS将方法注册到JS环境的bridge,OC调用JS的核心方法就是 [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];,JS环境收到消息后,通过方法WebViewJavascriptBridge._handleMessageFromObjC();将消息进行解析,调用注册在bridge的方法。
  • OC将方法注册到OC环境的bridge,JS调用OC的核心逻辑是,设置空白网页的src属性,从而被webView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler,OC通过核心方法- (void)flushMessageQueue:(NSString *)messageQueueString将传递数据进行解析。
  • 两边都是通过方法名找到对应的方法实现,然后通过ID来查找回调函数。

多阅读源码是好的一方面可以提升自己源码阅读水平,另一方面可以学习作者的一些好的设计思路。

参考:

github.com/marcuswesti…

github.com/lzyzsd/JsBr…

juejin.cn/post/684490…