Weex学习与实践(一):Weex,你需要知道的事

Weex学习与实践(二):iOS集成的tips

Weex学习与实践(三):iOS原理篇

本文主要介绍包括WeexSDK-iOS主要类介绍、Weex页面iOS端渲染流程、JS调用iOS方法

主要类

WXSDKEngine

WXSDKEngine主要用于初始化WeexSDK的环境

一开始会载入配置文件main.js并且注册一些默认的组件、模块以及handler

+ (void)initSDKEnviroment:(NSString *)script
{
   
    [self _registerDefaultComponents];
    [self _registerDefaultModules];
    [self _registerDefaultHandlers];
    
    [[WXSDKManager bridgeMgr] executeJsFramework:script];
}

在executeJsFramework前,会设置后JSContext的一些回调,例如

    _jsContext[@"callNative"] = callNativeBlock;

以方便JS调用native的方法。

executeJsFramework调用的是JSContext的evaluateScript方法,把main.js运行到jS的环境里面,之后再通过JSValue调用invokeMethod方法,把前面所有的components,modules,handlers注册进入JS环境

WXSDKInstance

一个WXSDKInstance就对应一个UIViewController,对应一个weex页面。

主要用来渲染页面,一般通过renderWithURL方法,然后能够接收一些回调和一些视图相关的方法

onCreate //根视图rootView创建的时候
renderFinish//视图渲染完成
componentForRef //通过视图索引拿到对应的组件视图

WXBridgeManager

WXBridgeManager 是JS与iOS通过JSCore交互的类,相关的类还有WXBridgeContext、WXJSCoreBridge。

比如调用JS


- (void)executeJsMethod:(WXBridgeMethod *)method
{
    if (!method) return;
    
    __weak typeof(self) weakSelf = self;
    WXPerformBlockOnBridgeThread(^(){
        [weakSelf.bridgeCtx executeJsMethod:method];
    });
}

JS调用native的话需要通过WXJSCoreBridge的registerCallNative方法

WXComponent

组件基类,自己实现iOS端的组件需要继承它。相关的还有负责组件初始化的工厂类WXComponentFactory,以及WXComponentManager

WXModuleProtocol

自定义module需要实现的协议

weex页面iOS端渲染流程

首先在ViewController里的render放初始化WXSDKInstance,因为render会支持实时刷新,所以每次都需要先销毁这个实例。

    [_instance destroyInstance];
    _instance = [[WXSDKInstance alloc] init];

然后WXSDKManager会保存instanceId

        [WXSDKManager storeInstance:self forID:_instanceId];

然后会调用renderWithURL方法来载入script,在这里会判断是本地文件还是需要从服务器下载,

- (void)renderWithURL:(NSURL *)url options:(NSDictionary *)options data:(id)data{
	    if ([url isFileURL]) {
        //from local
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSString *path = [url path];
            NSData *scriptData = [[NSFileManager defaultManager] contentsAtPath:path];
            NSString *script = [[NSString alloc] initWithData:scriptData encoding:NSUTF8StringEncoding];
            [weakSelf renderView:script options:newOptions data:data];
        });
    }else{
    	//from server
    }
}

然后就会根据script文件渲染视图

[weakSelf renderView:script options:newOptions data:data];

在这个方法里面首先会创建根视图,当创建完成时WXSDKInstance会收到onCreate的回调

    //TODO WXRootView
    WXPerformBlockOnMainThread(^{
        self.rootView = [[WXView alloc] initWithFrame:self.frame];
        if(self.onCreate) {
            self.onCreate(self.rootView);
        }
    });

之后再通过bridge调用JS方法来开始创建实例

    [self callJSMethod:@"createInstance" args:args];

然后这里会判断JSFramework也就是js有没有加载完成,然后再通过WXJSBridge的JSContext来执行js方法,这里调用的就是js的createInstance方法,args里面主要就是instanceID,we文件转化的js文件,和options。

- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
    [[_jsContext globalObject] invokeMethod:method withArguments:args];
}

最后js会调用JSContext的callCreateFinish回调,最后调用WXSDKInstance的createFinish方法来结束页面的渲染

JS调用iOS方法

首先要注册一个组件

    [self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")];

注册module的时候 会通过下面方法

+ (void)registerModule:(NSString *)name withClass:(Class)clazz
{
    WXAssert(name && clazz, @"Fail to register the module, please check if the parameters are correct !");
    
    NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz];
    NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName];
    
    [[WXSDKManager bridgeMgr] registerModules:dict];
}

把所有通过宏注册的方法发送给js端

WX_EXPORT_METHOD(@selector(createBody:))

这会把方法暴露出来,并且方法名字是”wx_export_method_“加代码所在行号,wx_export_method_25

组件、模块 是给js端用的,而handler则是给objc自己用的,所以不用发送消息给js端

然后通过methodForSelector拿到WX_EXPORT_METHOD方法的返回值,并且保存到methods中

- (void)registerModuleMethods {

            if ([currentClass respondsToSelector:selector]) {
                method = ((NSString* (*)(id, SEL))[currentClass methodForSelector:selector])(currentClass, selector);
            }
            [_methods setObject:method forKey:name];

}

然后拿到WXModuleConfig组成的_moduleMap之后再发送给JS端

    [[WXSDKManager bridgeMgr] registerModules:dict];

这里就是前面提到的调用JSContext的invokeMethod把内容发送到JS端

最后需要自己callNative的回调,当JS调用时就会传值到这里

- (void)registerCallNative:(WXJSCallNative)callNative
{
    NSInteger (^callNativeBlock)(JSValue *, JSValue *, JSValue *) = ^(JSValue *instance, JSValue *tasks, JSValue *callback){
        NSString *instanceId = [instance toString];
        NSArray *tasksArray = [tasks toArray];
        NSString *callbackId = [callback toString];
        
        return callNative(instanceId, tasksArray, callbackId);
    };
    
    _jsContext[@"callNative"] = callNativeBlock;
}

tasks里面包括方法的一些相关信息,包括module(比如dom),method(比如updateFinish),args

weex-devtool-iOS

weex-devtool-iOS 其实是 PonyDebugger的衍生品。

使用PonyDebugger调试iOS应用

FLEXNetworkObserver