static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self registerDefaults]; [[WXSDKManager bridgeMgr] executeJsFramework:script]; }); WX_MONITOR_PERF_END(WXPTInitalizeSync) } + (void)registerDefaults { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self _registerDefaultComponents]; [self _registerDefaultModules]; [self _registerDefaultHandlers]; }); }
下面是注册的组件。
+ (void)_registerDefaultComponents { [self registerComponent:@"container" withClass:NSClassFromString(@"WXDivComponent") withProperties:nil]; [self registerComponent:@"div" withClass:NSClassFromString(@"WXComponent") withProperties:nil]; [self registerComponent:@"text" withClass:NSClassFromString(@"WXTextComponent") withProperties:nil]; [self registerComponent:@"image" withClass:NSClassFromString(@"WXImageComponent") withProperties:nil]; [self registerComponent:@"scroller" withClass:NSClassFromString(@"WXScrollerComponent") withProperties:nil]; [self registerComponent:@"list" withClass:NSClassFromString(@"WXListComponent") withProperties:nil]; [self registerComponent:@"recycler" withClass:NSClassFromString(@"WXRecyclerComponent") withProperties:nil]; [self registerComponent:@"waterfall" withClass:NSClassFromString(@"WXRecyclerComponent") withProperties:nil]; [self registerComponent:@"header" withClass:NSClassFromString(@"WXHeaderComponent")]; [self registerComponent:@"cell" withClass:NSClassFromString(@"WXCellComponent")]; [self registerComponent:@"embed" withClass:NSClassFromString(@"WXEmbedComponent")]; [self registerComponent:@"a" withClass:NSClassFromString(@"WXAComponent")]; [self registerComponent:@"select" withClass:NSClassFromString(@"WXSelectComponent")]; [self registerComponent:@"switch" withClass:NSClassFromString(@"WXSwitchComponent")]; [self registerComponent:@"input" withClass:NSClassFromString(@"WXTextInputComponent")]; [self registerComponent:@"video" withClass:NSClassFromString(@"WXVideoComponent")]; [self registerComponent:@"indicator" withClass:NSClassFromString(@"WXIndicatorComponent")]; [self registerComponent:@"slider" withClass:NSClassFromString(@"WXCycleSliderComponent")]; [self registerComponent:@"cycleslider" withClass:NSClassFromString(@"WXCycleSliderComponent")]; [self registerComponent:@"web" withClass:NSClassFromString(@"WXWebComponent")]; [self registerComponent:@"loading" withClass:NSClassFromString(@"WXLoadingComponent")]; [self registerComponent:@"loading-indicator" withClass:NSClassFromString(@"WXLoadingIndicator")]; [self registerComponent:@"refresh" withClass:NSClassFromString(@"WXRefreshComponent")]; [self registerComponent:@"textarea" withClass:NSClassFromString(@"WXTextAreaComponent")];	[self registerComponent:@"canvas" withClass:NSClassFromString(@"WXCanvasComponent")]; [self registerComponent:@"slider-neighbor" withClass:NSClassFromString(@"WXSliderNeighborComponent")]; [self registerComponent:@"recycle-list" withClass:NSClassFromString(@"WXRecycleListComponent")]; [self registerComponent:@"cell-slot" withClass:NSClassFromString(@"WXCellSlotComponent") withProperties: @{@"append":@"tree", @"isTemplate":@YES}]; }
注册的模块:
+ (void)_registerDefaultModules { [self registerModule:@"dom" withClass:NSClassFromString(@"WXDomModule")]; [self registerModule:@"locale" withClass:NSClassFromString(@"WXLocaleModule")]; [self registerModule:@"navigator" withClass:NSClassFromString(@"WXNavigatorModule")]; [self registerModule:@"stream" withClass:NSClassFromString(@"WXStreamModule")]; [self registerModule:@"animation" withClass:NSClassFromString(@"WXAnimationModule")]; [self registerModule:@"modal" withClass:NSClassFromString(@"WXModalUIModule")]; [self registerModule:@"webview" withClass:NSClassFromString(@"WXWebViewModule")]; [self registerModule:@"instanceWrap" withClass:NSClassFromString(@"WXInstanceWrap")]; [self registerModule:@"timer" withClass:NSClassFromString(@"WXTimerModule")]; [self registerModule:@"storage" withClass:NSClassFromString(@"WXStorageModule")]; [self registerModule:@"clipboard" withClass:NSClassFromString(@"WXClipboardModule")]; [self registerModule:@"globalEvent" withClass:NSClassFromString(@"WXGlobalEventModule")]; [self registerModule:@"canvas" withClass:NSClassFromString(@"WXCanvasModule")]; [self registerModule:@"picker" withClass:NSClassFromString(@"WXPickerModule")]; [self registerModule:@"meta" withClass:NSClassFromString(@"WXMetaModule")]; [self registerModule:@"webSocket" withClass:NSClassFromString(@"WXWebSocketModule")]; [self registerModule:@"voice-over" withClass:NSClassFromString(@"WXVoiceOverModule")]; }
Handlers:
+ (void)_registerDefaultHandlers { [self registerHandler:[WXResourceRequestHandlerDefaultImpl new] withProtocol:@protocol(WXResourceRequestHandler)]; [self registerHandler:[WXNavigationDefaultImpl new] withProtocol:@protocol(WXNavigationProtocol)]; [self registerHandler:[WXURLRewriteDefaultImpl new] withProtocol:@protocol(WXURLRewriteProtocol)]; }
WXSDKManager的bridgeMgr是WXBridgeManager类。它为需要执行JS代码创建了一个线程,这个线程是不会终止的。
- (void)executeJsFramework:(NSString *)script { if (!script) return; __weak typeof(self) weakSelf = self; WXPerformBlockOnBridgeThread(^(){ [weakSelf.bridgeCtx executeJsFramework:script]; }); } #pragma mark Thread Management - (void)_runLoopThread { [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; while (!_stopRunning) { @autoreleasepool { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } } } + (NSThread *)jsThread { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ WXBridgeThread = [[NSThread alloc] initWithTarget:[[self class]sharedManager] selector:@selector(_runLoopThread) object:nil]; [WXBridgeThread setName:WX_BRIDGE_THREAD_NAME]; if(WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { [WXBridgeThread setQualityOfService:[[NSThread mainThread] qualityOfService]]; } else { [WXBridgeThread setThreadPriority:[[NSThread mainThread] threadPriority]]; } [WXBridgeThread start]; }); return WXBridgeThread; } void WXPerformBlockOnBridgeThread(void (^block)(void)) { [WXBridgeManager _performBlockOnBridgeThread:block]; } + (void)_performBlockOnBridgeThread:(void (^)(void))block { if ([NSThread currentThread] == [self jsThread]) { block(); } else { [self performSelector:@selector(_performBlockOnBridgeThread:) onThread:[self jsThread] withObject:[block copy] waitUntilDone:NO]; } }
这个WXBridgeManager还可以注册模块、组件、Handler,以及执行JS方法。它的作用是本地native代码和JS框架的交互。这里的JS框架就是刚才本地的native-bundle-main.js文件。
现在我们来看下组件的注册:
- (void)registerComponents:(NSArray *)components { if (!components) return; __weak typeof(self) weakSelf = self; WXPerformBlockOnBridgeThread(^(){ [weakSelf.bridgeCtx registerComponents:components]; }); }
它的bridgeCtx是WXBridgeContext这个类。
- (void)registerComponents:(NSArray *)components { WXAssertBridgeThread(); if(!components) return; [self callJSMethod:@"registerComponents" args:@[components]]; } - (void)callJSMethod:(NSString *)method args:(NSArray *)args { if (self.frameworkLoadFinished) { [self.jsBridge callJSMethod:method args:args]; } else { [_methodQueue addObject:@{@"method":method, @"args":args}]; } }
一开始native-bundle-main.js还没加载完成,调用native-bundle-main.js里面的JS方法会放到_methodQueue数组中,等当native-bundle-main.js加载完成后再执行这些方法。下面是补充之前加载native-bundle-main.js的代码:
- (void)executeJsFramework:(NSString *)script { WXAssertBridgeThread(); WXAssertParam(script); WX_MONITOR_PERF_START(WXPTFrameworkExecute); [self.jsBridge executeJSFramework:script]; WX_MONITOR_PERF_END(WXPTFrameworkExecute); if ([self.jsBridge exception]) { NSString *exception = [[self.jsBridge exception] toString]; NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_SDK_INIT_JSFM_INIT_FAILED] %@",exception]; [WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_SDK_INIT" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_SDK_INIT] function:@"" exception:errMsg extParams:nil]; WX_MONITOR_FAIL(WXMTJSFramework, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg); } else { WX_MONITOR_SUCCESS(WXMTJSFramework); //the JSFramework has been load successfully. self.frameworkLoadFinished = YES; [self executeAllJsService]; JSValue *frameworkVersion = [self.jsBridge callJSMethod:@"getJSFMVersion" args:nil]; if (frameworkVersion && [frameworkVersion isString]) { [WXAppConfiguration setJSFrameworkVersion:[frameworkVersion toString]]; } if (script) { [WXAppConfiguration setJSFrameworkLibSize:[script lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; } //execute methods which has been stored in methodQueue temporarily. for (NSDictionary *method in _methodQueue) { [self callJSMethod:method[@"method"] args:method[@"args"]]; } [_methodQueue removeAllObjects]; WX_MONITOR_PERF_END(WXPTInitalize); }; }
WXBridgeContext的jsBridge是WXJSCoreBridge。
- (id)jsBridge { WXAssertBridgeThread(); _debugJS = [WXDebugTool isDevToolDebug]; Class bridgeClass = _debugJS ? NSClassFromString(@"WXDebugger") : [WXJSCoreBridge class]; if (_jsBridge && [_jsBridge isKindOfClass:bridgeClass]) { return _jsBridge; } if (_jsBridge) { [_methodQueue removeAllObjects]; _frameworkLoadFinished = NO; } _jsBridge = _debugJS ? [NSClassFromString(@"WXDebugger") alloc] : [[WXJSCoreBridge alloc] init]; [self registerGlobalFunctions]; return _jsBridge; }
在获取jsBridge时,还注册了全局函数,[self registerGlobalFunctions];这个全局函数是JS函数,当解析本地的bundlejs的文件是会调用这个全局函数。
- (void)registerGlobalFunctions { __weak typeof(self) weakSelf = self; [_jsBridge registerCallNative:^NSInteger(NSString *instance, NSArray *tasks, NSString *callback) { return [weakSelf invokeNative:instance tasks:tasks callback:callback]; }]; [_jsBridge registerCallAddElement:^NSInteger(NSString *instanceId, NSString *parentRef, NSDictionary *elementData, NSInteger index) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:elementData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"addElement" options:nil]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [WXTracingManager startTracingWithInstanceId:instanceId ref:elementData[@"ref"] className:nil name:WXTDomCall phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTDOMThread}]; [manager startComponentTasks]; [manager addComponent:elementData toSupercomponent:parentRef atIndex:index appendingInTree:NO]; [WXTracingManager startTracingWithInstanceId:instanceId ref:elementData[@"ref"] className:nil name:WXTDomCall phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTDOMThread}]; }); return 0; }]; [_jsBridge registerCallCreateBody:^NSInteger(NSString *instanceId, NSDictionary *bodyData) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"] className:nil name:WXTDomCall phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTDOMThread}]; [manager startComponentTasks]; [manager createRoot:bodyData]; [WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"] className:nil name:WXTDomCall phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTDOMThread}]; }); return 0; }]; [_jsBridge registerCallRemoveElement:^NSInteger(NSString *instanceId, NSString *ref) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"removeElement" options:nil]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTDomCall phase:WXTracingBegin functionName:@"removeElement" options:@{@"threadName":WXTDOMThread}]; [manager startComponentTasks]; [manager removeComponent:ref]; [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTDomCall phase:WXTracingEnd functionName:@"removeElement" options:@{@"threadName":WXTDOMThread}]; }); return 0; }]; [_jsBridge registerCallMoveElement:^NSInteger(NSString *instanceId,NSString *ref,NSString *parentRef,NSInteger index) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"moveElement" options:nil]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [manager startComponentTasks]; [manager moveComponent:ref toSuper:parentRef atIndex:index]; }); return 0; }]; [_jsBridge registerCallUpdateAttrs:^NSInteger(NSString *instanceId,NSString *ref,NSDictionary *attrsData) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTDomCall phase:WXTracingBegin functionName:@"updateAttrs" options:@{@"threadName":WXTDOMThread}]; [manager startComponentTasks]; [manager updateAttributes:attrsData forComponent:ref]; [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTDomCall phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTDOMThread}]; }); return 0; }]; [_jsBridge registerCallUpdateStyle:^NSInteger(NSString *instanceId,NSString *ref,NSDictionary *stylesData) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateStyles" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [manager startComponentTasks]; [manager updateStyles:stylesData forComponent:ref]; }); return 0; }]; [_jsBridge registerCallAddEvent:^NSInteger(NSString *instanceId,NSString *ref,NSString *event) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [manager startComponentTasks]; [manager addEvent:event toComponent:ref]; [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"addEvent" options:nil]; }); return 0; }]; [_jsBridge registerCallRemoveEvent:^NSInteger(NSString *instanceId,NSString *ref,NSString *event) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [manager startComponentTasks]; [manager removeEvent:event fromComponent:ref]; [WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"removeEvent" options:nil]; }); return 0; }]; [_jsBridge registerCallCreateFinish:^NSInteger(NSString *instanceId) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:nil className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createFinish" options:@{@"threadName":WXTJSBridgeThread}]; WXPerformBlockOnComponentThread(^{ WXComponentManager *manager = instance.componentManager; if (!manager.isValid) { return; } [WXTracingManager startTracingWithInstanceId:instanceId ref:nil className:nil name:WXTDomCall phase:WXTracingBegin functionName:@"createFinish" options:@{@"threadName":WXTDOMThread}]; [manager startComponentTasks]; [manager createFinish]; [WXTracingManager startTracingWithInstanceId:instanceId ref:nil className:nil name:WXTDomCall phase:WXTracingEnd functionName:@"createFinish" options:@{@"threadName":WXTDOMThread}]; }); return 0; }]; [_jsBridge registerCallNativeModule:^NSInvocation*(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *arguments, NSDictionary *options) { WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if (!instance) { WXLogInfo(@"instance not found for callNativeModule:%@.%@, maybe already destroyed", moduleName, methodName); return nil; } WXModuleMethod *method = [[WXModuleMethod alloc] initWithModuleName:moduleName methodName:methodName arguments:arguments options:options instance:instance]; if(![moduleName isEqualToString:@"dom"] && instance.needPrerender){ [WXPrerenderManager storePrerenderModuleTasks:method forUrl:instance.scriptURL.absoluteString]; return nil; } return [method invoke]; }]; [_jsBridge registerCallNativeComponent:^void(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options) { WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; WXComponentMethod *method = [[WXComponentMethod alloc] initWithComponentRef:componentRef methodName:methodName arguments:args instance:instance]; [method invoke]; }]; }
这些全局函数主要是监测元素添加、删除,样式、属性的变化,以及事件的注册回调。
我们回到WeexSDKManager的setup方法,在初始化WeexSDK后,会调用[self loadCustomContainWithScannerWithUrl:]方法创建一个控制器并显示。
+ (void)loadCustomContainWithScannerWithUrl:(NSURL *)url { UIViewController *demo = [[WXDemoViewController alloc] init]; ((WXDemoViewController *)demo).url = url; [[UIApplication sharedApplication] delegate].window.rootViewController = [[WXRootViewController alloc] initWithRootViewController:demo]; }
这里的url是本地bundlejs文件下的index.js的路径。这个index.js文件也就是我们在weex项目中编写的index.vue入口文件的node解析后的文件。
我们看下WeexSDK提供的WXBaseViewController,因为这个控制器是Weex的Navigator模块在push新界面时使用的。
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.automaticallyAdjustsScrollViewInsets = NO; [self _renderWithURL:_sourceURL]; if ([self.navigationController isKindOfClass:[WXRootViewController class]]) { [self.navigationController setNavigationBarHidden:YES animated:YES]; } }
这里的_sourceURL就是之前的index.js的入口文件URL。这个它会根据这个URL指定的文件进行界面渲染。
- (void)_renderWithURL:(NSURL *)sourceURL { if (!sourceURL) { return; } [_instance destroyInstance]; if([WXPrerenderManager isTaskReady:[self.sourceURL absoluteString]]){ _instance = [WXPrerenderManager instanceFromUrl:self.sourceURL.absoluteString]; } _instance = [[WXSDKInstance alloc] init]; _instance.frame = CGRectMake(0.0f, 0.0f, self.view.bounds.size.width, self.view.bounds.size.height); _instance.pageObject = self; _instance.pageName = sourceURL.absoluteString; _instance.viewController = self; NSString *newURL = nil; if ([sourceURL.absoluteString rangeOfString:@"?"].location != NSNotFound) { newURL = [NSString stringWithFormat:@"%@&random=%d", sourceURL.absoluteString, arc4random()]; } else { newURL = [NSString stringWithFormat:@"%@?random=%d", sourceURL.absoluteString, arc4random()]; } [_instance renderWithURL:[NSURL URLWithString:newURL] options:@{@"bundleUrl":sourceURL.absoluteString} data:nil]; __weak typeof(self) weakSelf = self; _instance.onCreate = ^(UIView *view) { [weakSelf.weexView removeFromSuperview]; weakSelf.weexView = view; [weakSelf.view addSubview:weakSelf.weexView]; }; _instance.onFailed = ^(NSError *error) { }; _instance.renderFinish = ^(UIView *view) { [weakSelf _updateInstanceState:WeexInstanceAppear]; }; if([WXPrerenderManager isTaskReady:[self.sourceURL absoluteString]]){ WX_MONITOR_INSTANCE_PERF_START(WXPTJSDownload, _instance); WX_MONITOR_INSTANCE_PERF_END(WXPTJSDownload, _instance); WX_MONITOR_INSTANCE_PERF_START(WXPTFirstScreenRender, _instance); WX_MONITOR_INSTANCE_PERF_START(WXPTAllRender, _instance); [WXPrerenderManager renderFromCache:[self.sourceURL absoluteString]]; return; } }
这里的WXSDKInstance(_instance)就是页面的实例,我们在自定义组件、模块都会用到这个实例并且通过viewController属性获取当前控制器。这个实例会进行界面渲染([_instance renderWithURL:])。
- (void)renderWithURL:(NSURL *)url options:(NSDictionary *)options data:(id)data { if (!url) { WXLogError(@"Url must be passed if you use renderWithURL"); return; } self.needValidate = [[WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)] needValidate:url]; WXResourceRequest *request = [WXResourceRequest requestWithURL:url resourceType:WXResourceTypeMainBundle referrer:@"" cachePolicy:NSURLRequestUseProtocolCachePolicy]; [self _renderWithRequest:request options:options data:data]; [WXTracingManager startTracingWithInstanceId:self.instanceId ref:nil className:nil name:WXTNetworkHanding phase:WXTracingBegin functionName:@"renderWithURL" options:@{@"bundleUrl":url?[url absoluteString]:@"",@"threadName":WXTMainThread}]; } - (void)_renderWithRequest:(WXResourceRequest *)request options:(NSDictionary *)options data:(id)data; { NSURL *url = request.URL; _scriptURL = url; _jsData = data; NSMutableDictionary *newOptions = [options mutableCopy] ?: [NSMutableDictionary new]; if (!newOptions[bundleUrlOptionKey]) { newOptions[bundleUrlOptionKey] = url.absoluteString; } // compatible with some wrong type, remove this hopefully in the future. if ([newOptions[bundleUrlOptionKey] isKindOfClass:[NSURL class]]) { WXLogWarning(@"Error type in options with key:bundleUrl, should be of type NSString, not NSURL!"); newOptions[bundleUrlOptionKey] = ((NSURL*)newOptions[bundleUrlOptionKey]).absoluteString; } _options = [newOptions copy]; if (!self.pageName || [self.pageName isEqualToString:@""]) { self.pageName = url.absoluteString ? : @""; } request.userAgent = [WXUtility userAgent]; WX_MONITOR_INSTANCE_PERF_START(WXPTJSDownload, self); __weak typeof(self) weakSelf = self; _mainBundleLoader = [[WXResourceLoader alloc] initWithRequest:request];; _mainBundleLoader.onFinished = ^(WXResourceResponse *response, NSData *data) { __strong typeof(weakSelf) strongSelf = weakSelf; NSError *error = nil; if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) { error = [NSError errorWithDomain:WX_ERROR_DOMAIN code:((NSHTTPURLResponse *)response).statusCode userInfo:@{@"message":@"status code error."}]; if (strongSelf.onFailed) { strongSelf.onFailed(error); } } if (strongSelf.onJSDownloadedFinish) { strongSelf.onJSDownloadedFinish(response, request, data, error); } if (error) { WXJSExceptionInfo * jsExceptionInfo = [[WXJSExceptionInfo alloc] initWithInstanceId:@"" bundleUrl:[request.URL absoluteString] errorCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_JS_DOWNLOAD] functionName:@"_renderWithRequest:options:data:" exception:[error localizedDescription] userInfo:nil]; [WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo]; return; } if (!data) { NSString *errorMessage = [NSString stringWithFormat:@"Request to %@ With no data return", request.URL]; WX_MONITOR_FAIL_ON_PAGE(WXMTJSDownload, WX_ERR_JSBUNDLE_DOWNLOAD, errorMessage, strongSelf.pageName); WXJSExceptionInfo * jsExceptionInfo = [[WXJSExceptionInfo alloc] initWithInstanceId:@"" bundleUrl:[request.URL absoluteString] errorCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_JS_DOWNLOAD] functionName:@"_renderWithRequest:options:data:" exception:@"no data return" userInfo:nil]; [WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo]; if (strongSelf.onFailed) { strongSelf.onFailed(error); } return; } NSString *jsBundleString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if (!jsBundleString) { WX_MONITOR_FAIL_ON_PAGE(WXMTJSDownload, WX_ERR_JSBUNDLE_STRING_CONVERT, @"data converting to string failed.", strongSelf.pageName) return; } if (!strongSelf.userInfo) { strongSelf.userInfo = [NSMutableDictionary new]; } strongSelf.userInfo[@"jsMainBundleStringContentLength"] = @([jsBundleString length]); strongSelf.userInfo[@"jsMainBundleStringContentMd5"] = [WXUtility md5:jsBundleString]; WX_MONITOR_SUCCESS_ON_PAGE(WXMTJSDownload, strongSelf.pageName); WX_MONITOR_INSTANCE_PERF_END(WXPTJSDownload, strongSelf); [strongSelf _renderWithMainBundleString:jsBundleString]; [WXTracingManager setBundleJSType:jsBundleString instanceId:weakSelf.instanceId]; }; _mainBundleLoader.onFailed = ^(NSError *loadError) { NSString *errorMessage = [NSString stringWithFormat:@"Request to %@ occurs an error:%@", request.URL, loadError.localizedDescription]; WX_MONITOR_FAIL_ON_PAGE(WXMTJSDownload, [loadError.domain isEqualToString:NSURLErrorDomain] && loadError.code == NSURLErrorNotConnectedToInternet ? WX_ERR_NOT_CONNECTED_TO_INTERNET : WX_ERR_JSBUNDLE_DOWNLOAD, errorMessage, weakSelf.pageName); if (weakSelf.onFailed) { weakSelf.onFailed(error); } }; [_mainBundleLoader start]; }
最终WXResourceLoader资源加载器会加载这个index.js文件的内容。最后判断成功后进行渲染。([strongSelf _renderWithMainBundleString:jsBundleString])
- (void)_renderWithMainBundleString:(NSString *)mainBundleString { if (!self.instanceId) { WXLogError(@"Fail to find instance!"); return; } if (![WXUtility isBlankString:self.pageName]) { WXLog(@"Start rendering page:%@", self.pageName); } else { WXLogWarning(@"WXSDKInstance's pageName should be specified."); idjsExceptionHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXJSExceptionProtocol)]; if ([jsExceptionHandler respondsToSelector:@selector(onRuntimeCheckException:)]) { WXRuntimeCheckException * runtimeCheckException = [WXRuntimeCheckException new]; runtimeCheckException.exception = @"We highly recommend you to set pageName.\n Using WXSDKInstance * instance = [WXSDKInstance new]; instance.pageName = @\"your page name\" to fix it"; [jsExceptionHandler onRuntimeCheckException:runtimeCheckException]; } } WX_MONITOR_INSTANCE_PERF_START(WXPTFirstScreenRender, self); WX_MONITOR_INSTANCE_PERF_START(WXPTAllRender, self); NSMutableDictionary *dictionary = [_options mutableCopy]; if ([WXLog logLevel] >= WXLogLevelLog) { dictionary[@"debug"] = @(YES); } if ([WXDebugTool getReplacedBundleJS]) { mainBundleString = [WXDebugTool getReplacedBundleJS]; } //TODO WXRootView WXPerformBlockOnMainThread(^{ _rootView = [[WXRootView alloc] initWithFrame:self.frame]; _rootView.instance = self; if(self.onCreate) { self.onCreate(_rootView); } }); // ensure default modules/components/handlers are ready before create instance [WXSDKEngine registerDefaults]; [[NSNotificationCenter defaultCenter] postNotificationName:WX_SDKINSTANCE_WILL_RENDER object:self]; [self _handleConfigCenter]; _needDestroy = YES; [WXTracingManager startTracingWithInstanceId:self.instanceId ref:nil className:nil name:WXTExecJS phase:WXTracingBegin functionName:@"renderWithMainBundleString" options:@{@"threadName":WXTMainThread}]; [[WXSDKManager bridgeMgr] createInstance:self.instanceId template:mainBundleString options:dictionary data:_jsData]; [WXTracingManager startTracingWithInstanceId:self.instanceId ref:nil className:nil name:WXTExecJS phase:WXTracingEnd functionName:@"renderWithMainBundleString" options:@{@"threadName":WXTMainThread}]; WX_MONITOR_PERF_SET(WXPTBundleSize, [mainBundleString lengthOfBytesUsingEncoding:NSUTF8StringEncoding], self); }
这里会先创建一个WXRootView的根视图,并确保注册了SDK默认的组件、模块、Handler。之后调用WXSDKManager的bridgeMgr创建实例。这个bridgeMgr就是之前的WXBridgeManager。
- (void)createInstance:(NSString *)instance template:(NSString *)temp options:(NSDictionary *)options data:(id)data { if (!instance || !temp) return; if (![self.instanceIdStack containsObject:instance]) { if ([options[@"RENDER_IN_ORDER"] boolValue]) { [self.instanceIdStack addObject:instance]; } else { [self.instanceIdStack insertObject:instance atIndex:0]; } } __weak typeof(self) weakSelf = self; WXPerformBlockOnBridgeThread(^(){ [WXTracingManager startTracingWithInstanceId:instance ref:nil className:nil name:WXTExecJS phase:WXTracingBegin functionName:@"createInstance" options:@{@"threadName":WXTJSBridgeThread}]; [weakSelf.bridgeCtx createInstance:instance template:temp options:options data:data]; [WXTracingManager startTracingWithInstanceId:instance ref:nil className:nil name:WXTExecJS phase:WXTracingEnd functionName:@"createInstance" options:@{@"threadName":WXTJSBridgeThread}]; }); }
确保在JSBridge线程下执行,这里的bridgeCtx就是之前的WXBridgeContext,它主要是JS运行的上下文。所有需要调用JS框架native-bundle-main.js的方法都在这个上下文实现。下面是bridgeCtx创建实例的代码:
- (void)createInstance:(NSString *)instance template:(NSString *)temp options:(NSDictionary *)options data:(id)data { WXAssertBridgeThread(); WXAssertParam(instance); if (![self.insStack containsObject:instance]) { if ([options[@"RENDER_IN_ORDER"] boolValue]) { [self.insStack addObject:instance]; } else { [self.insStack insertObject:instance atIndex:0]; } } //create a sendQueue bind to the current instance NSMutableArray *sendQueue = [NSMutableArray array]; [self.sendQueue setValue:sendQueue forKey:instance]; NSArray *args = nil; if (data){ args = @[instance, temp, options ?: @{}, data]; } else { args = @[instance, temp, options ?: @{}]; } WX_MONITOR_INSTANCE_PERF_START(WXFirstScreenJSFExecuteTime, [WXSDKManager instanceForID:instance]); WX_MONITOR_INSTANCE_PERF_START(WXPTJSCreateInstance, [WXSDKManager instanceForID:instance]); [self callJSMethod:@"createInstance" args:args]; WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instance]); } - (void)callJSMethod:(NSString *)method args:(NSArray *)args { if (self.frameworkLoadFinished) { [self.jsBridge callJSMethod:method args:args]; } else { [_methodQueue addObject:@{@"method":method, @"args":args}]; } }
这里的jsBridge就是之前的WXCoreBridge,之前创建它时,注册了各种全局全局函数,这个全局函数包括新增和删除元素,样式和属性变化以及实例的创建的回调,还有本地自定义模块和组件的调用回调。下面是jsBridge的callJSMethod:
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args { WXLogDebug(@"Calling JS... method:%@, args:%@", method, args); return [[_jsContext globalObject] invokeMethod:method withArguments:args]; }
这里的_jsContext是iOS框架自带的JSContext,这个类是在JavaScriptCore框架内,如果想了解JSContext和JSValue的使用,请到Apple的开发者网站了解。
上面的代码其实就是执行native-bundle-main.js文件所在的上下文中的createInstance函数(JS函数)。这个函数会解析index.js入口文件的内容并调用之前在JSContext注册的全局回调函数。下面是之前注册的CreateBody回调:
- (void)registerCallCreateBody:(WXJSCallCreateBody)callCreateBody { id WXJSCallCreateBodyBlock = ^(JSValue *instanceId, JSValue *body,JSValue *ifCallback) { NSString *instanceIdString = [instanceId toString]; NSDictionary *bodyData = [body toDictionary]; WXLogDebug(@"callCreateBody...%@, %@,", instanceIdString, bodyData); [WXTracingManager startTracingWithInstanceId:instanceIdString ref:bodyData[@"ref"] className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTJSBridgeThread}]; return [JSValue valueWithInt32:(int32_t)callCreateBody(instanceIdString, bodyData) inContext:[JSContext currentContext]]; }; _jsContext[@"callCreateBody"] = WXJSCallCreateBodyBlock; } [_jsBridge registerCallCreateBody:^NSInteger(NSString *instanceId, NSDictionary *bodyData) { // Temporary here , in order to improve performance, will be refactored next version. WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId]; if(![weakSelf checkInstance:instance]) { return -1; } [WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"