weex的基本工作原理
我们在weex项目中编写的代码最终通过编译打包后形成bundlejs文件。
iOS项目通过Weex SDK解析bundlejs文件,进行native的界面渲染。
Weex SDK的组件渲染
应用完成启动时会初始化WeexSDK;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[WeexSDKManager setup];
[self.window makeKeyAndVisible];
// Override point for customization after application launch.
[self startSplashScreen];
return YES;
}
@implementation WeexSDKManager
+ (void)setup;
{
NSURL *url = nil;
#if DEBUG
//If you are debugging in device , please change the host to current IP of your computer.
url = [NSURL URLWithString:BUNDLE_URL];
#else
url = [NSURL URLWithString:BUNDLE_URL];
#endif
NSString * entryURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"WXEntryBundleURL"];
if (entryURL) {
if ([entryURL hasPrefix:@"http"]) {
url = [NSURL URLWithString:entryURL];
} else {
url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",[[NSBundle bundleForClass:self] resourceURL].absoluteString, entryURL]];
}
}
#ifdef UITEST
url = [NSURL URLWithString:UITEST_HOME_URL];
#endif
[self initWeexSDK];
[self loadCustomContainWithScannerWithUrl:url];
}
+ (void)initWeexSDK
{
[WXAppConfiguration setAppGroup:@"AliApp"];
[WXAppConfiguration setAppName:@"WeexDemo"];
[WXAppConfiguration setAppVersion:@"1.8.3"];
[WXAppConfiguration setExternalUserAgent:@"ExternalUA"];
[WXSDKEngine initSDKEnvironment];
[WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
#ifdef DEBUG
[WXLog setLogLevel:WXLogLevelLog];
#endif
}
+ (void)initSDKEnvironment
{
NSString *filePath = [[NSBundle bundleForClass:self] pathForResource:@"native-bundle-main" ofType:@"js"];
NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[WXSDKEngine initSDKEnvironment:script];
上面的这段代码会加载本地WeexSDK下的native-bundle-main.js文件。
之后会注册组件、模块、handlers。以及执行本地的js文件。
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<WXBridgeProtocol>)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.");
id<WXJSExceptionProtocol> jsExceptionHandler = [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"] 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的registCallCreateBody在WXBridgeContext,上面的方法是WXJSCoreBridge的实例方法。在这个回调中WXComponentManager开启组件任务之后创建根组件。
- (void)startComponentTasks
{
[self _awakeDisplayLink];
}
- (void)_awakeDisplayLink
{
WXAssertComponentThread();
if(_displayLink && _displayLink.paused) {
_displayLink.paused = NO;
}
}
这里是开启displayLink,用于屏幕刷新时的界面更新。
- (void)createRoot:(NSDictionary *)data
{
WXAssertComponentThread();
WXAssertParam(data);
_rootComponent = [self _buildComponentForData:data supercomponent:nil];
[self _initRootCSSNode];
NSArray *subcomponentsData = [data valueForKey:@"children"];
if (subcomponentsData) {
BOOL appendTree = [_rootComponent.attributes[@"append"] isEqualToString:@"tree"];
for(NSDictionary *subcomponentData in subcomponentsData){
[self _recursivelyAddComponent:subcomponentData toSupercomponent:_rootComponent atIndex:-1 appendingInTree:appendTree];
}
}
__weak typeof(self) weakSelf = self;
WX_MONITOR_INSTANCE_PERF_END(WXFirstScreenJSFExecuteTime, self.weexInstance);
[self _addUITask:^{
[WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
__strong typeof(self) strongSelf = weakSelf;
strongSelf.weexInstance.rootView.wx_component = strongSelf->_rootComponent;
[strongSelf.weexInstance.rootView addSubview:strongSelf->_rootComponent.view];
[WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
}];
}
创建跟组件会对它的子组件也一并创建,最后weexInstance的rootView添加跟组件的view,完成整个界面的渲染。这个rootView是之前创建控制器时配置的rootView。
- (WXComponent *)_buildComponentForData:(NSDictionary *)data supercomponent:(WXComponent *)supercomponent
{
NSString *ref = data[@"ref"];
NSString *type = data[@"type"];
NSDictionary *styles = data[@"style"];
NSDictionary *attributes = data[@"attr"];
NSArray *events = data[@"event"];
if (self.weexInstance.needValidate) {
id<WXValidateProtocol> validateHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)];
if (validateHandler) {
WXComponentValidateResult* validateResult;
if ([validateHandler respondsToSelector:@selector(validateWithWXSDKInstance:component:supercomponent:)]) {
validateResult = [validateHandler validateWithWXSDKInstance:self.weexInstance component:type supercomponent:supercomponent];
}
if (validateResult==nil || !validateResult.isSuccess) {
type = validateResult.replacedComponent? validateResult.replacedComponent : @"div";
WXLogError(@"%@",[validateResult.error.userInfo objectForKey:@"errorMsg"]);
}
}
}
WXComponentConfig *config = [WXComponentFactory configWithComponentName:type];
BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate);
NSDictionary *bindingStyles;
NSDictionary *bindingAttibutes;
NSDictionary *bindingEvents;
NSDictionary *bindingProps;
if (isTemplate) {
bindingProps = [self _extractBindingProps:&attributes];
bindingStyles = [self _extractBindings:&styles];
bindingAttibutes = [self _extractBindings:&attributes];
bindingEvents = [self _extractBindingEvents:&events];
}
Class clazz = NSClassFromString(config.clazz);;
WXComponent *component = [[clazz alloc] initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:self.weexInstance];
if (isTemplate) {
component->_isTemplate = YES;
[component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents];
}
WXAssert(component, @"Component build failed for data:%@", data);
[_indexDict setObject:component forKey:component.ref];
[component readyToRender];// notify redyToRender event when init
return component;
}
- (void)_recursivelyAddComponent:(NSDictionary *)componentData toSupercomponent:(WXComponent *)supercomponent atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree
{
WXComponent *component = [self _buildComponentForData:componentData supercomponent:supercomponent];
if (!supercomponent.subcomponents) {
index = 0;
} else {
index = (index == -1 ? supercomponent->_subcomponents.count : index);
}
[supercomponent _insertSubcomponent:component atIndex:index];
// use _lazyCreateView to forbid component like cell's view creating
if(supercomponent && component && supercomponent->_lazyCreateView) {
component->_lazyCreateView = YES;
}
if (!component->_isTemplate) {
__weak typeof(self) weakSelf = self;
[self _addUITask:^{
[WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
[supercomponent insertSubview:component atIndex:index];
[WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
}];
}
NSArray *subcomponentsData = [componentData valueForKey:@"children"];
BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"];
// if ancestor is appending tree, child should not be laid out again even it is appending tree.
for(NSDictionary *subcomponentData in subcomponentsData){
[self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree];
}
[component _didInserted];
if (appendTree) {
// If appending tree,force layout in case of too much tasks piling up in syncQueue
[self _layoutAndSyncUI];
}
}
上面是构建组件和关联父组件以及添加子组件的代码。插入子组件就是addView的操作(每个组件都有一个view)。通过组件配置WXComponentConfig获取对应组件的原生对象的类(class)来创建组件。
WXComponent基础组件。
@interface WXComponent : NSObject <NSCopying>
///--------------------------------------
/// @name Component Hierarchy Management
///--------------------------------------
/**
* @abstract Initializes a new component using the specified properties.
*
* @param ref the identity string of component
* @param type component type
* @param styles component's styles
* @param attributes component's attributes
* @param events component's events
* @param weexInstance the weexInstance with which the component associated
*
* @return A WXComponent instance.
*/
- (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance;
WXComponent必须实现loadView方法。以上就是Weex的组件渲染过程,如果想了解weex的组件可以直接查看weex的WXComponent源码。