工作感悟

RN 杂谈(一)

2018-07-22  本文已影响0人  Message_id

导读:

  • 来源:React Native是Facebook在2013年启动的一个内部项目,2015年在F8大会上发布的,这个项目目前在GitHub已经几万的star和上万的fork了(https://github.com/facebook/react-native)。
  • 解决的问题:设计初衷是成为 JavaScript 和原生应用之间的一个异步,序列化和批处理的“桥梁”,慢慢发展到后面的一个跨平台移动APP开发框架,提供了一种多平台同时运行的语言环境,使你能够在JavaScript 和react的基础上获得完全一致的开发体验。
  • 初步对比:【RN】 JS引擎,RN在iOS使用系统自带的JSCore,安卓上是外部引入的JSCore,渲染使react,理念“Learn Once ,Write Anywhere” ,RN 并做不到一份代码很好的适配 iOS 和安卓的所有机型和系统,需要你根据具体情况去做适配; 【Weex】中iOS也用系统自带的JSCore,安卓中使用V8,渲染使用的vue;理念“Write Once Run Everywhere”;两者最后的渲染都是纯native控件完成。

官网:http://facebook.github.io/react-native
中文官网:https://reactnative.cn

基本原理:

应用架构

1531755416006-ba10345c-9402-452f-9ec4-66255f4db269.jpeg

以iOS为例

  1. OC 调用 JS
JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];

这里的 JSContext指的是 JavaScript 代码的运行环境,通过 evaluateScript即可执行 JavaScript 代码并获取返回结果。

  1. JS 调用 OC
  1. OC 与 JS 函数互相回调实现
1531848751155-f3cce384-82b2-4742-8ed7-0081c9a04842.png

OC暴露给JS方法

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

这个类在 load 方法中就会调用 RCTRegisterModule 方法注册自己:

void RCTRegisterModule(Class moduleClass)
{
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    RCTModuleClasses = [NSMutableArray new];
  });
  [RCTModuleClasses addObject:moduleClass];
}

因此,React Native 可以通过 RCTModuleClasses 拿到所有暴露给 JavaScript 的类。下一步操作是遍历这个数组,然后生成 RCTModuleData 对象

for (Class moduleClass in RCTGetModuleClasses()) {
    RCTModuleData *moduleData = [[RCTModuleData alloc]initWithModuleClass:moduleClass  bridge:self];
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
}

RCTModuleData 对象是模块配置表的主要组成部分。如果把模块配置表想象成一个数组,那么每一个元素就是一个 RCTModuleData 对象;
这个对象保存了 Module 的名字,常量等基本信息,最重要的属性是一个数组,保存了所有需要暴露给 JavaScript 的方法;</span></span>

- (NSArray<id<RCTBridgeMethod>> *)methods
{
  if (!_methods) {
    NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];

    if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
      [moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
    }

    unsigned int methodCount;
    Class cls = _moduleClass;
    while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
      Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);

      for (unsigned int i = 0; i < methodCount; i++) {
        Method method = methods[I];
        SEL selector = method_getName(method);
        if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
          IMP imp = method_getImplementation(method);
          auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector);
          id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod
                                                                                 moduleClass:_moduleClass];
          [moduleMethods addObject:moduleMethod];
        }
      }

      free(methods);
      cls = class_getSuperclass(cls);
    }

    _methods = [moduleMethods copy];
  }
  return _methods;
}

因此 Objective-C 管理模块配置表的逻辑是:Bridge 持有一个数组,数组中保存了所有的模块的 RCTModuleData 对象。只要给定 ModuleId 和 MethodId 就可以唯一确定要调用的方法。

上一篇 下一篇

猜你喜欢

热点阅读