XMWebView 1.0.1

XMWebView 1.0.1

xiaoma 维护。



XMWebView 1.0.1

XMWebView

CI Status Version License Platform

一个轻量级的 Hybrid 框架

原生 -> JS

WKWebView 仅有一个 evaluateJavaScript:completionHandler: 方法来调用 JS,JS 调用原生需要两步

JS -> 原生

  1. 通过 WKUserContentControlleraddScriptMessageHandler: 方法注册一个 MessageHandler
  2. JS 通过 window.webkit.messageHandlers.XM_JS2Native.postMessage(parameters); 来调用原生。

该框架只是对这两个方法的封装,以更加统一的方式,来打通原生 - JS

封装之后,框架如下 info

JSAssembler

JS 组装器,用来组装原生调用 JS 字符串, XMWebViewJSDefaultAssembler 实现了 XMWebViewJSAssemblerProtocol 协议,提供了一个默认的组装器

 XM_Native2JS({name: foo, 
               params:{
                  foo1: bar1,
                  foo2: bar2,
               }});

可以实现 XMWebViewJSAssemblerProtocol 来定义自己的组装器

JSInvoker

JSInvoker 会将每次 JS 调用封装为 XMWebViewJSInvokeOperation 并放入队列中执行,队列有两个,invokJSQueueWithLoadingState 需要在 webView 加载完成后开始执行;而 invokJSQueueIgnoreLoadingState 则会立即执行。

XMWebViewJSDispatcher

XMWebViewJSDispatcher 是一个派发器,用于分发 JS 调用到 Native 方法。

JSParser

XMWebViewJSDefaultJSParser 实现 XMWebViewJSParserProtocol 协议,用于解析 JS 传递过来的数据,该解析器解析如下默认数据。

JS 调用 Native 的数据格式如下。

 XM_JS2Native({name: foo, 
               params:{
                  foo1: bar1,
                  foo2: bar2,
               }});

Native 收到的回调数据如下。

message.body = @{
    name: foo
    params: @{
        foo1: bar1,
        foo2: bar2,
    }
 }

XMWebViewJSDefaultJSParser 将解析出类名 XMWebViewFooHandler,参数 @{@"foo1": bar1, @"foo2": bar2}

XMWebViewFooHandler 是继承自 XMWebViewJSDefaultHandlerOperationoperation,这 operation 会被加入主队列中执行。

XMWebViewJSDefaultHandlerOperation

该类是处理业务逻辑的基类,应继承此类来处理自己的业务逻辑。该类已经处理好各种状态机和 JS 传递过来的参数的绑定,子类只需实现 -handleBusiness: 来处理自己的业务,处理完毕后,调用 -callJSBackWithParams: 回调给 JS。

JSCallBackAssembler

XMWebViewJSDefaultCallBackAssembler 实现 XMWebViewJSCallBackAssemblerProtocol 协议,用于构造一个 NSString 来执行 JS。用户可以自定义构造器来定义回调给 JS 的格式。

webView 启动时,Native 主动注入一段 JS,以便 JS 能够调用 Native,当 Native 回调时,将调用 window.XM_NativeCallBackNameMap.funcName(params) 来触发 MessageChannel 的 port1 发送消息,Promise 中的 port2 的 onmessage 将会被调用,然后执行 resolve。

var XM_JS2Native_MessageChannelMap = {};
function XM_JS2Native(parameters) {
    var channel = new MessageChannel(); // 创建一个 MessageChannel
    if(!window.XM_NativeCallBackNameMap) {
        window.XM_NativeCallBackNameMap = {};
    }
    var id = parameters.name + "_" + Math.random();
    parameters.id = id;
    XM_JS2Native_MessageChannelMap[id] = channel;

    window.XM_NativeCallBackNameMap[parameters.name] = function(nativeValue) {
        if (nativeValue.id && XM_JS2Native_MessageChannelMap[nativeValue.id]) {
            XM_JS2Native_MessageChannelMap[nativeValue.id].port1.postMessage(nativeValue);
            delete XM_JS2Native_MessageChannelMap[nativeValue.id];
        }
    };
    window.webkit.messageHandlers.XM_JS2Native.postMessage(parameters);
    return new Promise(function(resolve, reject) {
        channel.port2.onmessage = function(e) {
            var data = e.data;
            resolve(data);
            channel = null;
        };
    });
}

使用方式

JS

XM_JS2Native({name: test, params:{foo1:bar1, foo2:bar2}}).then(nativeValue => {
    // 当 Native 回调的时候,这里会被出发
})

Native

XMWebViewTestHandler.h

@interface XMWebViewTestHandler : XMWebViewJSDefaultHandlerOperation

@end

XMWebViewTestHandler.m

#import "XMWebViewTestHandler.h"

@implementation XMWebViewTestHandler

- (void)handleBusiness {
    NSLog(@"param: %@", self.parameters);
    [self callJSBackWithParams:@{@"arg1": @"foo", @"arg2": @"bar"}];
}

@end

如有疑问,欢迎提交 issue。