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源碼。