0.从工作原理谈起

Weex 表面上是一个客户端技术,但实际上它串联起了从本地开发、云端部署到分发的整个链路。开发者首先可在本地像编写 web 页面一样编写一个 app 的界面,然后通过命令行工具将之编译成一段 JavaScript 代码,生成一个 Weex 的 JS bundle;同时,开发者可以将生成的 JS bundle 部署至云端,然后通过网络请求或预下发的方式加载至用户的移动应用客户端;在移动应用客户端里,Weex SDK 会准备好一个 JavaScript 执行环境,并且在用户打开一个 Weex 页面时在这个执行环境中执行相应的 JS bundle,并将执行过程中产生的各种命令发送到 native 端进行界面渲染、数据存储、网络通信、调用设备功能及用户交互响应等功能;同时,如果用户希望使用浏览器访问这个界面,那么他可以在浏览器里打开一个相同的 web 页面,这个页面和移动应用使用相同的页面源代码,但被编译成适合Web展示的JS Bundle,通过浏览器里的 JavaScript 引擎及 Weex SDK 运行起来的。

上面是Weex官方的介绍。下面对Native端的原理做一下细分:

下面我们开始对Native端的解析,做一下分析。

1.WeexSDK初始化

我们新建一个Weex项目之后,通过“weex platform add ios”,可以添加iOS的工程代码。打开该工程,可以看到如下内容:

  1. + (void)initWeexSDK
  2. {
  3. [WXAppConfiguration setAppGroup:@"AliApp"];
  4. [WXAppConfiguration setAppName:@"WeexDemo"];
  5. [WXAppConfiguration setAppVersion:@"1.8.3"];
  6. [WXAppConfiguration setExternalUserAgent:@"ExternalUA"];
  7.  
  8. [WXSDKEngine initSDKEnvironment];
  9.  
  10. [WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
  11.  
  12. #ifdef DEBUG
  13. [WXLog setLogLevel:WXLogLevelLog];
  14. #endif
  15. }

该代码一般在application: didFinishLaunchingWithOptions:中进行调用,用于初始化WeexSDK,下面我们来看一下这部分的源码实现,通过查看源码,更深入的理解WeexSDK。

2.WXAppConfiguration

  1. @interface WXAppConfiguration : NSObject
  2.  
  3. /**
  4. * @abstract Group or organization of your app, default value is nil.
  5. */
  6. + (NSString *)appGroup;
  7. + (void)setAppGroup:(NSString *) appGroup;
  8.  
  9. /**
  10. * @abstract Name of your app, default is value for CFBundleDisplayName in main bundle.
  11. */
  12. + (NSString *)appName;
  13. + (void)setAppName:(NSString *)appName;
  14.  
  15. /**
  16. * @abstract Version of your app, default is value for CFBundleShortVersionString in main bundle.
  17. */
  18. + (NSString *)appVersion;
  19. + (void)setAppVersion:(NSString *)appVersion;
  20.  
  21. /**
  22. * @abstract External user agent of your app, all requests sent by weex will set the user agent on header, default value is nil.
  23. */
  24. + (NSString *)externalUserAgent;
  25. + (void)setExternalUserAgent:(NSString *)userAgent;
  26.  
  27. /**
  28. * @abstract JSFrameworkVersion
  29. */
  30. + (NSString *)JSFrameworkVersion;
  31. + (void)setJSFrameworkVersion:(NSString *)JSFrameworkVersion;
  32.  
  33. /**
  34. + * @abstract JSFrameworkLibSize
  35. + */
  36. + (NSUInteger)JSFrameworkLibSize;
  37. + (void)setJSFrameworkLibSize:(NSUInteger)JSFrameworkLibSize;
  38.  
  39. /*
  40. * @abstract customizeProtocolClasses
  41. */
  42. + (NSArray*)customizeProtocolClasses;
  43. + (void)setCustomizeProtocolClasses:(NSArray*)customizeProtocolClasses;
  44.  
  45. @end

从.h文件可以看到,该类是用来用来记录App配置信息的。所有方法都是类方法,内部实现是用单例实现的,.h用类方法是为了方便调用。

3.实质初始化

  1. [WXSDKEngine initSDKEnvironment];

该方法都做了哪些事情呢?

  1. + (void)initSDKEnvironment
  2. {
  3. // 加载本地的main.js
  4. NSString *filePath = [[NSBundle bundleForClass:self] pathForResource:@"native-bundle-main" ofType:@"js"];
  5. NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
  6. // 初始化SDK环境
  7. [WXSDKEngine initSDKEnvironment:script];
  8.  
  9. // 模拟器版本特殊代码
  10. #if TARGET_OS_SIMULATOR
  11. static dispatch_once_t onceToken;
  12. dispatch_once(&onceToken, ^{
  13. [WXSimulatorShortcutManager registerSimulatorShortcutWithKey:@"i" modifierFlags:UIKeyModifierCommand | UIKeyModifierAlternate action:^{
  14. NSURL *URL = [NSURL URLWithString:@"http://localhost:8687/launchDebugger"];
  15. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  16.  
  17. NSURLSession *session = [NSURLSession sharedSession];
  18. NSURLSessionDataTask *task = [session dataTaskWithRequest:request
  19. completionHandler:
  20. ^(NSData *data, NSURLResponse *response, NSError *error) {
  21. // ...
  22. }];
  23.  
  24. [task resume];
  25. WXLogInfo(@"Launching browser...");
  26.  
  27. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  28. [self connectDebugServer:@"ws://localhost:8687/debugger/0/renderer"];
  29. });
  30.  
  31. }];
  32. });
  33. #endif
  34. }
  35.  
  36. + (void)initSDKEnvironment:(NSString *)script
  37. {
  38. // 打点记录状态
  39. WX_MONITOR_PERF_START(WXPTInitalize)
  40. WX_MONITOR_PERF_START(WXPTInitalizeSync)
  41.  
  42. if (!script || script.length <= ) {
  43. NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_SDK_INIT_JSFM_INIT_FAILED] script don't exist:%@",script];
  44. [WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_SDK_INIT" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_SDK_INIT] function:@"initSDKEnvironment" exception:errMsg extParams:nil];
  45. WX_MONITOR_FAIL(WXMTJSFramework, WX_ERR_JSFRAMEWORK_LOAD, errMsg);
  46. return;
  47. }
  48. static dispatch_once_t onceToken;
  49. dispatch_once(&onceToken, ^{
  50. // 注册Components,Modules,Handlers
  51. [self registerDefaults];
  52. // 执行JsFramework
  53. [[WXSDKManager bridgeMgr] executeJsFramework:script];
  54. });
  55. // 打点记录状态
  56. WX_MONITOR_PERF_END(WXPTInitalizeSync)
  57.  
  58. }

从源码可以看到,初始化总共做了四件事情:

  • WXMonitor监视器记录状态;
  • WXSDKEngine的初始化
  • 加载本地的main.js;
  • 模拟器WXSimulatorShortcutManager连接本地server。

3.1WXMonitor

WXMonitor是一个普通的对象,它里面只存储了一个线程安全的字典WXThreadSafeMutableDictionary。

  1. @interface WXThreadSafeMutableDictionary<KeyType, ObjectType> : NSMutableDictionary
  2.  
  3. @property (nonatomic, strong) dispatch_queue_t queue;
  4. @property (nonatomic, strong) NSMutableDictionary* dict;
  5.  
  6. @end

WXMonitor在整个Weex里面担任的职责是记录下各个操作的tag值和记录成功和失败的原因

在WXSDKInstance初始化之前,所有的全局的global操作都会放在WXMonitor的WXThreadSafeMutableDictionary中。当WXSDKInstance初始化之后,即WXPerformanceTag中instance以下的所有操作都会放在WXSDKInstance的performanceDict中。

3.2加载本地的main.js

main.js是经过webpack压缩之后的文件,它是Native这边的JS Framework。

3.3WXSDKEngine的初始化

  1. + (void)registerDefaults
  2. {
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. [self _registerDefaultComponents];
  6. [self _registerDefaultModules];
  7. [self _registerDefaultHandlers];
  8. });
  9. }

从上面可以看到,WXSDKEngine在初始化的时候,分别注册了Components、Modules、Handlers。这是 Weex 与 Native 应用层交互最核心的部分,可以理解为“组件”。其中 Component 是为了映射 Html 的一些标签,Module 中是提供一些 Native 的方法供 Weex 调用,Handler 是一些协议的实现。

注册完 Weex 默认的“组件” 之后,注入3.2小节的那段 JS,这个时候 Vue 的标签和动作才能被 Weex 所识别和转换。

3.4执行JsFramework

  1. [[WXSDKManager bridgeMgr] executeJsFramework:script];

WXSDKManager会调用WXBridgeManager去执行SDK里面的main.js文件。

  1. - (void)executeJsFramework:(NSString *)script
  2. {
  3. if (!script) return;
  4.  
  5. __weak typeof(self) weakSelf = self;
  6. WXPerformBlockOnBridgeThread(^(){
  7. [weakSelf.bridgeCtx executeJsFramework:script];
  8. });
  9. }

WXBridgeManager通过WXBridgeContext调用executeJsFramework:方法,这里的方法调用也是在子线程中进行的。

  1. - (void)executeJsFramework:(NSString *)script
  2. {
  3. WXAssertBridgeThread();
  4. WXAssertParam(script);
  5.  
  6. WX_MONITOR_PERF_START(WXPTFrameworkExecute);
  7.  
  8. [self.jsBridge executeJSFramework:script];
  9.  
  10. WX_MONITOR_PERF_END(WXPTFrameworkExecute);
  11.  
  12. if ([self.jsBridge exception]) {
  13. NSString *exception = [[self.jsBridge exception] toString];
  14. NSMutableString *errMsg = [NSMutableString stringWithFormat:@"[WX_KEY_EXCEPTION_SDK_INIT_JSFM_INIT_FAILED] %@",exception];
  15. [WXExceptionUtils commitCriticalExceptionRT:@"WX_KEY_EXCEPTION_SDK_INIT" errCode:[NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_SDK_INIT] function:@"" exception:errMsg extParams:nil];
  16. WX_MONITOR_FAIL(WXMTJSFramework, WX_ERR_JSFRAMEWORK_EXECUTE, errMsg);
  17. } else {
  18. WX_MONITOR_SUCCESS(WXMTJSFramework);
  19. //the JSFramework has been load successfully.
  20. // 至此JSFramework是完全加载完成了
  21. self.frameworkLoadFinished = YES;
  22. // 执行所有注册的JsService
  23. [self executeAllJsService];
  24. // 获取JSFramework版本号
  25. JSValue *frameworkVersion = [self.jsBridge callJSMethod:@"getJSFMVersion" args:nil];
  26. if (frameworkVersion && [frameworkVersion isString]) {
  27. // 把版本号存入WXAppConfiguration中
  28. [WXAppConfiguration setJSFrameworkVersion:[frameworkVersion toString]];
  29. }
  30.  
  31. if (script) {
  32. [WXAppConfiguration setJSFrameworkLibSize:[script lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
  33. }
  34.  
  35. //execute methods which has been stored in methodQueue temporarily.
  36. // 执行之前缓存在_methodQueue数组里面的所有方法
  37. for (NSDictionary *method in _methodQueue) {
  38. [self callJSMethod:method[@"method"] args:method[@"args"]];
  39. }
  40.  
  41. [_methodQueue removeAllObjects];
  42. // 至此,初始化工作完成
  43. WX_MONITOR_PERF_END(WXPTInitalize);
  44. };
  45. }

WX_MONITOR_PERF_START是在操作之前标记WXPTFrameworkExecute,执行完JSFramework以后,用WX_MONITOR_PERF_END标记执行完成。

  1. - (void)executeJSFramework:(NSString *)frameworkScript
  2. {
  3. WXAssertParam(frameworkScript);
  4. if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
  5. [_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@"native-bundle-main.js"]];
  6. }else{
  7. [_jsContext evaluateScript:frameworkScript];
  8. }
  9. }
加载JSFramework的核心代码在这里,通过JSContext执行evaluateScript:来加载JSFramework。由于这里并没有返回值,所以加载的JSFramework的目的仅仅是声明了里面的所有方法,并没有调用。这也符合OC加载其他Framework的过程,加载只是加载到内存中,Framework里面的方法可以随时被调用,而不是一加载就调用其所有的方法。

加载完成JSFramework以后,就要开始加载之前缓存的JSService和JSMethod。JSService是在jsServiceQueue中缓存的(默认SDK里面,是没有的)。JSMethod(组件、模块中缓存的)是在methodQueue中缓存的。

  1. - (void)executeAllJsService
  2. {
  3. for(NSDictionary *service in _jsServiceQueue) {
  4. NSString *script = [service valueForKey:@"script"];
  5. NSString *name = [service valueForKey:@"name"];
  6. [self executeJsService:script withName:name];
  7. }
  8.  
  9. [_jsServiceQueue removeAllObjects];
  10. }
  11.  
  12. for (NSDictionary *method in _methodQueue) {
  13. [self callJSMethod:method[@"method"] args:method[@"args"]];
  14. }
  15.  
  16. - (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args
  17. {
  18. WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
  19. return [[_jsContext globalObject] invokeMethod:method withArguments:args];
  20. }
由于_methodQueue里面装的都是全局的js方法,所以需要调用invokeMethod: withArguments:去执行。当这一切都加载完成,SDK的初始化工作就基本完成了,这里就会标记上WXPTInitalize结束。

这里再补充讨论一下,jsBridge第一次是如何被加载进来的?

  1. - (id<WXBridgeProtocol>)jsBridge
  2. {
  3. WXAssertBridgeThread();
  4. _debugJS = [WXDebugTool isDevToolDebug];
  5.  
  6. Class bridgeClass = _debugJS ? NSClassFromString(@"WXDebugger") : [WXJSCoreBridge class];
  7.  
  8. if (_jsBridge && [_jsBridge isKindOfClass:bridgeClass]) {
  9. return _jsBridge;
  10. }
  11.  
  12. if (_jsBridge) {
  13. [_methodQueue removeAllObjects];
  14. _frameworkLoadFinished = NO;
  15. }
  16.  
  17. _jsBridge = _debugJS ? [NSClassFromString(@"WXDebugger") alloc] : [[WXJSCoreBridge alloc] init];
  18.  
  19. [self registerGlobalFunctions];
  20.  
  21. return _jsBridge;
  22. }

第一次进入这个函数没有jsBridge实例的时候,会先生成WXJSCoreBridge的实例,然后紧接着注册全局的函数。等第二次再调用这个函数的时候,_jsBridge已经是WXJSCoreBridge类型了,就会直接return,下面的语句也不会再重复执行了。

  1. typedef NSInteger(^WXJSCallNative)(NSString *instance, NSArray *tasks, NSString *callback);
  2. typedef NSInteger(^WXJSCallAddElement)(NSString *instanceId, NSString *parentRef, NSDictionary *elementData, NSInteger index);
  3. typedef NSInteger(^WXJSCallCreateBody)(NSString *instanceId, NSDictionary *bodyData);
  4. typedef NSInteger(^WXJSCallRemoveElement)(NSString *instanceId,NSString *ref);
  5. typedef NSInteger(^WXJSCallMoveElement)(NSString *instanceId,NSString *ref,NSString *parentRef,NSInteger index);
  6. typedef NSInteger(^WXJSCallUpdateAttrs)(NSString *instanceId,NSString *ref,NSDictionary *attrsData);
  7. typedef NSInteger(^WXJSCallUpdateStyle)(NSString *instanceId,NSString *ref,NSDictionary *stylesData);
  8. typedef NSInteger(^WXJSCallAddEvent)(NSString *instanceId,NSString *ref,NSString *event);
  9. typedef NSInteger(^WXJSCallRemoveEvent)(NSString *instanceId,NSString *ref,NSString *event);
  10. typedef NSInteger(^WXJSCallCreateFinish)(NSString *instanceId);
  11. typedef NSInvocation *(^WXJSCallNativeModule)(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *args, NSDictionary *options);
  12. typedef void (^WXJSCallNativeComponent)(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options);

上面的闭包,是OC封装暴露给JS的。对应的全局函数:

  1. - (void)registerCallNative:(WXJSCallNative)callNative
  2. {
  3. JSValue* (^callNativeBlock)(JSValue *, JSValue *, JSValue *) = ^JSValue*(JSValue *instance, JSValue *tasks, JSValue *callback){
  4. NSString *instanceId = [instance toString];
  5. NSArray *tasksArray = [tasks toArray];
  6. NSString *callbackId = [callback toString];
  7. WXLogDebug(@"Calling native... instance:%@, tasks:%@, callback:%@", instanceId, tasksArray, callbackId);
  8. return [JSValue valueWithInt32:(int32_t)callNative(instanceId, tasksArray, callbackId) inContext:[JSContext currentContext]];
  9. };
  10.  
  11. _jsContext[@"callNative"] = callNativeBlock;
  12. }
  13.  
  14. - (void)registerCallAddElement:(WXJSCallAddElement)callAddElement
  15. {
  16. id callAddElementBlock = ^(JSValue *instanceId, JSValue *ref, JSValue *element, JSValue *index, JSValue *ifCallback) {
  17.  
  18. NSString *instanceIdString = [instanceId toString];
  19. NSDictionary *componentData = [element toDictionary];
  20. NSString *parentRef = [ref toString];
  21. NSInteger insertIndex = [[index toNumber] integerValue];
  22. [WXTracingManager startTracingWithInstanceId:instanceIdString ref:componentData[@"ref"] className:nil name:WXTJSCall phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTJSBridgeThread,@"componentData":componentData}];
  23. WXLogDebug(@"callAddElement...%@, %@, %@, %ld", instanceIdString, parentRef, componentData, (long)insertIndex);
  24.  
  25. return [JSValue valueWithInt32:(int32_t)callAddElement(instanceIdString, parentRef, componentData, insertIndex) inContext:[JSContext currentContext]];
  26. };
  27.  
  28. _jsContext[@"callAddElement"] = callAddElementBlock;
  29. }
  30.  
  31. - (void)registerCallNativeModule:(WXJSCallNativeModule)callNativeModuleBlock
  32. {
  33. _jsContext[@"callNativeModule"] = ^JSValue *(JSValue *instanceId, JSValue *moduleName, JSValue *methodName, JSValue *args, JSValue *options) {
  34. NSString *instanceIdString = [instanceId toString];
  35. NSString *moduleNameString = [moduleName toString];
  36. NSString *methodNameString = [methodName toString];
  37. NSArray *argsArray = [args toArray];
  38. NSDictionary *optionsDic = [options toDictionary];
  39.  
  40. WXLogDebug(@"callNativeModule...%@,%@,%@,%@", instanceIdString, moduleNameString, methodNameString, argsArray);
  41.  
  42. NSInvocation *invocation = callNativeModuleBlock(instanceIdString, moduleNameString, methodNameString, argsArray, optionsDic);
  43. JSValue *returnValue = [JSValue wx_valueWithReturnValueFromInvocation:invocation inContext:[JSContext currentContext]];
  44. [WXTracingManager startTracingWithInstanceId:instanceIdString ref:nil className:nil name:moduleNameString phase:WXTracingInstant functionName:methodNameString options:nil];
  45. return returnValue;
  46. };
  47. }
  48.  
  49. - (void)registerCallNativeComponent:(WXJSCallNativeComponent)callNativeComponentBlock
  50. {
  51. _jsContext[@"callNativeComponent"] = ^void(JSValue *instanceId, JSValue *componentName, JSValue *methodName, JSValue *args, JSValue *options) {
  52. NSString *instanceIdString = [instanceId toString];
  53. NSString *componentNameString = [componentName toString];
  54. NSString *methodNameString = [methodName toString];
  55. NSArray *argsArray = [args toArray];
  56. NSDictionary *optionsDic = [options toDictionary];
  57.  
  58. WXLogDebug(@"callNativeComponent...%@,%@,%@,%@", instanceIdString, componentNameString, methodNameString, argsArray);
  59.  
  60. callNativeComponentBlock(instanceIdString, componentNameString, methodNameString, argsArray, optionsDic);
  61. };
  62. }

由于JS的方法的写法,多个参数是依次写在小括号里面的,和OC多个参数中间用:号隔开是不一样的,所有在暴露给JS的时候,需要把Block再包装一层。包装的4个方法如上,最后把这4个方法注入到JSContext中。

如上图,灰色的就是OC本地传入的Block,外面在包一层,变成JS的方法,注入到JSContext中。

WeexSDK源码分析(iOS)的更多相关文章

  1. iOS硬解H.264:-VideoToolboxDemo源码分析[草稿]

    来源:http://www.cnblogs.com/michaellfx/p/understanding_-VideoToolboxDemo.html iOS硬解H.264:-VideoToolbox ...

  2. iOS常用框架源码分析

    SDWebImage NSCache 类似可变字典,线程安全,使用可变字典自定义实现缓存时需要考虑加锁和释放锁 在内存不足时NSCache会自动释放存储的对象,不需要手动干预 NSCache的key不 ...

  3. jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

    事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...

  4. [Android实例] Scroll原理-附ScrollView源码分析

    想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内 ...

  5. Go Mobile 例子 basic 源码分析

    OpenGL ES(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集,针对手机.PDA和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广, ...

  6. AFNetworking源码分析

    来源:zongmumask 链接:http://www.jianshu.com/p/8eac5b1975de 简述 在iOS开发中,与直接使用苹果框架中提供的NSURLConnection或NSURL ...

  7. YYCache 源码分析(一)

    iOS 开发中总会用到各种缓存,YYCache或许是你最好的选择.性能上有优势,用法也很简单.作者ibireme曾经对比过同类轮子:http://blog.ibireme.com/2015/10/26 ...

  8. 安卓MonkeyRunner源码分析之启动

    在工作中因为要追求完成目标的效率,所以更多是强调实战,注重招式,关注怎么去用各种框架来实现目的.但是如果一味只是注重招式,缺少对原理这个内功的了解,相信自己很难对各种框架有更深入的理解. 从几个月前开 ...

  9. Appium Server源码分析之作为Bootstrap客户端

    Appium Server拥有两个主要的功能: 它是个http服务器,它专门接收从客户端通过基于http的REST协议发送过来的命令 他是bootstrap客户端:它接收到客户端的命令后,需要想办法把 ...

随机推荐

  1. PHP判断手机、电脑访问

    /*判断用户是手机访问还是电脑访问*/$useragent = $_SERVER['HTTP_USER_AGENT']; if (preg_match('/(android|bb\d+|meego). ...

  2. Spring MVC和Spring Boot的理解以及比较

    Spring MVC是什么?(1)Spring MVC是Spring提供的一个强大而灵活的模块式web框架.通过Dispatcher Servlet, ModelAndView 和 View Reso ...

  3. 高性能迷你React框架anujs1.0.5发布

    实现对createFactory的支持,优化scheduler与dispose机制,提供ReactShim文件,跑通公司内部4套测试 npm i anujs 或者使用架手架 https://githu ...

  4. 吴裕雄 python深度学习与实践(13)

    import numpy as np import matplotlib.pyplot as plt x_data = np.random.randn(10) print(x_data) y_data ...

  5. 2019/1/15 python基础学习

    一.列表切片a.快捷方式:spam[:3] ----表示从列表开始到3位置结束:拿到的内容是0,1,2没有3位置上的数字.spam[3:]b.列表的拼接.复制: 拼接:使用 + [1,2,3]+['A ...

  6. JVM系列2:垃圾收集器与内存分配策略

    垃圾收集是一个很大话题,本文也只是看了深入理解Java虚拟机总结了下垃圾收集的知识. 首先按照惯例,先上思维导图: 垃圾收集简而言之就是JVM帮我们清理掉内存区域不需要的数据.它主要负责清理堆中实例对 ...

  7. 详细分析MySQL事务日志(redo log和undo log) 表明了为何mysql不会丢数据

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  8. 从汇编层面解释switch语句判断快速的原因

      源码如下: #include <stdio.h>   void main(){     int flag;     flag=1;     switch (flag){         ...

  9. node.js中Buffer缓冲器的使用

    一.什么是Buffer Buffer缓冲器是用来存储输入和输出数据的一段内存.js语言没有二进制数据类型,在处理TCP和文件流的时候,就不是很方便了. 所以node.js提供了Buffer类来处理二进 ...

  10. http协议和四个层之间的关系

    TCP/IP协议的分层:应用层.传输层.网络层.数据链路层. ····应用层···· 决定了向用户提供应用服务时通信的活动.HTTP协议存在于该层.(FTP文件传输协议,DNS域名系统) ....传输 ...