iOS Principle:ReactNative(中)

简介: iOS Principle:ReactNative(中)

4.生成模块配置表并写入JavaScript端

复习一下 nativeRequireModuleConfig 这个 Block,它可以接受 ModuleName 并且生成详细的模块信息,但在前文中我们没有提到 JavaScript 是如何知道 Objective-C 要暴露哪些类的(目前只是 Objective-C 自己知道)。

这一步的操作就是为了让 JavaScript 获取所有模块的名字

- (NSString *)moduleConfig {
  NSMutableArray<NSArray *> *config = [NSMutableArray new];
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (self.executorClass == [RCTJSCExecutor class]) {
      [config addObject:@[moduleData.name]];
    } else {
      [config addObject:RCTNullIfNil(moduleData.config)];
    }
  }
  return RCTJSONStringify(@{
    @"remoteModuleConfig": config,
  }, NULL);
}

5.执行JavaScript代码

这一步也没什么技术难度可以,代码已经加载进了内存,该做的配置也已经完成,只要把 JavaScript 代码运行一遍即可。

运行代码时,第三步中所说的那些 Block 就会被执行,从而向 JavaScript 端写入配置信息。

至此,JavaScript 和 Objective-C 都具备了向对方交互的能力,准备工作也就全部完成了。


方法调用


如前文所述,在 React Native 中,Objective-C 和 JavaScript 的交互都是通过传递 ModuleId、MethodId 和 Arguments 进行的。以下是分情况讨论


OC 调用 JavaScript


也许你在其他文章中曾经多次听说 JavaScript 代码总是在一个单独的线程上面调用,它的实际含义是 Objective-C 会在单独的线程上运行 JavaScript 代码

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block {
  if ([NSThread currentThread] != _javaScriptThread) {
    [self performSelector:@selector(executeBlockOnJavaScriptQueue:)
                 onThread:_javaScriptThread withObject:block waitUntilDone:NO];
  } else {
    block();
  }
}

调用JavaScript的核心代码如下

- (void)_executeJSCall:(NSString *)method
             arguments:(NSArray *)arguments
              callback:(RCTJavaScriptCallback)onComplete{
    [self executeBlockOnJavaScriptQueue:^{
        // 获取 contextJSRef、methodJSRef、moduleJSRef
        resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, arguments.count, jsArgs, &errorJSRef);
        objcValue = /*resultJSRef 转换成 Objective-C 类型*/
        onComplete(objcValue, nil);
    }];
}

需要注意的是,这个函数名是我们要调用 JavaScript 的中转函数名,比如 callFunctionReturnFlushedQueue。也就是说它的作用其实是处理参数,而非真正要调用的 JavaScript 函数。

在实际使用的时候,我们可以这样发起对 JavaScript 的调用:

[_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                         body:@{ @"name": @"nmae"}];

这里的 Name 和 Body 参数分别表示要调用的 JavaScript 的函数名和参数。


JavaScript调用OC

在调用 Objective-C 代码时,如前文所述,JavaScript 会解析出方法的 ModuleId、MethodId 和 Arguments 并放入到 MessageQueue 中,等待 Objective-C 主动拿走,或者超时后主动发送给 Objective-C。

Objective-C 负责处理调用的方法是 handleBuffer,它的参数是一个含有四个元素的数组,每个元素也都是一个数组,分别存放了 ModuleId、MethodId、Params,第四个元素目测用处不大。

函数内部在每一次方调用中调用 _handleRequestNumber:moduleID:methodID:params 方法,通过查找模块配置表找出要调用的方法,并通过 runtime 动态的调用:

演示JavaScript调用OC方法:

//.h文件
#import <Foundation/Foundation.h>
#import "RCTBridge.h"
#import "RCTLog.h"
#import "EncryptUtil.h"
#import "RSA.h"
@interface CryptoExport : NSObject<RCTBridgeModule>
@end
//.m文件
#import "CryptoExport.h"
@implementation CryptoExport
RCT_EXPORT_MODULE()//必须定义的宏
RCT_EXPORT_METHOD(rsaEncryptValue:(NSString *)src withKey:(NSString *)rsaKey successCallback:(RCTResponseSenderBlock)successCallback){
  NSString *rsaValue = [RSA encryptString:src publicKey:rsaKey];
  successCallback(@[rsaValue]);
}
@end

每个oc的方法前必须加上 RCT_EXPORT_METHOD 宏,用来注册模块表。

在JavaScript中的调动如下

NativeModules.CryptoExport.rsaEncryptValue(value, rsaKey,function (rsaValue) {
    console.log(rsaValue)
});


React Native更新机制



image.png


之前我们说过,React是状态机,就是不停的去检查当前的状态,判断是否需要刷新。

调用this.setState

ReactClass.prototype.setState = function(newState) {
    this._reactInternalInstance.receiveComponent(null, newState);
}

调用内部receiveComponent方法,这里在接受元素的时候主要分三种情况:

  • 文本元素
  • 基本元素
  • 自定义元素

文本元素

ReactDOMTextComponent.prototype.receiveComponent(nextText, transaction) {
     //跟以前保存的字符串比较
    if (nextText !== this._currentElement) {
      this._currentElement = nextText;
      var nextStringText = '' + nextText;
      if (nextStringText !== this._stringText) {
        this._stringText = nextStringText;
        var commentNodes = this.getHostNode();
        // 替换文本元素
        DOMChildrenOperations.replaceDelimitedText(
          commentNodes[0],
          commentNodes[1],
          nextStringText
        );
      }
    }
  }

基本元素

ReactDOMComponent.prototype.receiveComponent = function(nextElement, transaction, context) {
    var prevElement = this._currentElement;
    this._currentElement = nextElement;
    this.updateComponent(transaction, prevElement, nextElement, context);
}

updateComponent 方法

ReactDOMComponent.prototype.updateComponent = function(transaction, prevElement, nextElement, context) {
    // 略.....
    //需要单独的更新属性
    this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag);
    //再更新子节点
    this._updateDOMChildren(
      lastProps,
      nextProps,
      transaction,
      context
    );
    // ......
}

自定义元素

ReactCompositeComponent.prototype.receiveComponent = function(nextElement, transaction, nextContext) {
    var prevElement = this._currentElement;
    var prevContext = this._context;
    this._pendingElement = null;
    this.updateComponent(
      transaction,
      prevElement,
      nextElement,
      prevContext,
      nextContext
    );
}

updateComponent 方法

ReactCompositeComponent.prototype.updateComponent = function(
    transaction,
    prevParentElement,
    nextParentElement,
    prevUnmaskedContext,
    nextUnmaskedContext
){
//省略
}

调用内部 _performComponentUpdate 方法

ReactCompositeComponent.prototype._updateRenderedComponentWithNextElement = function() {
    // 判定两个element需不需要更新
    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      // 如果需要更新,就继续调用子节点的receiveComponent的方法,传入新的element更新子节点。
      ReactReconciler.receiveComponent(
        prevComponentInstance,
        nextRenderedElement,
        transaction,
        this._processChildContext(context)
      );
    } else {
      // 卸载之前的子节点,安装新的子节点
      var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
      ReactReconciler.unmountComponent(
        prevComponentInstance,
        safely,
        false /* skipLifecycle */
      );
      var nodeType = ReactNodeTypes.getType(nextRenderedElement);
      this._renderedNodeType = nodeType;
      var child = this._instantiateReactComponent(
        nextRenderedElement,
        nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
      );
      this._renderedComponent = child;
      var nextMarkup = ReactReconciler.mountComponent(
        child,
        transaction,
        this._hostParent,
        this._hostContainerInfo,
        this._processChildContext(context),
        debugID
      );
    }

this._updateDOMChildren 方法内部调用diff算法


image.png


实现过程

_updateChildren: function(nextNestedChildrenElements, transaction, context) {
    var prevChildren = this._renderedChildren;
    var removedNodes = {};
    var mountImages = [];
    // 获取新的子元素数组
    var nextChildren = this._reconcilerUpdateChildren(
      prevChildren,
      nextNestedChildrenElements,
      mountImages,
      removedNodes,
      transaction,
      context
    );
    if (!nextChildren && !prevChildren) {
      return;
    }
    var updates = null;
    var name;
    var nextIndex = 0;
    var lastIndex = 0;
    var nextMountIndex = 0;
    var lastPlacedNode = null;
    for (name in nextChildren) {
      if (!nextChildren.hasOwnProperty(name)) {
        continue;
      }
      var prevChild = prevChildren && prevChildren[name];
      var nextChild = nextChildren[name];
      if (prevChild === nextChild) {
          // 同一个引用,说明是使用的同一个component,所以我们需要做移动的操作
          // 移动已有的子节点
          // NOTICE:这里根据nextIndex, lastIndex决定是否移动
        updates = enqueue(
          updates,
          this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex)
        );
        // 更新lastIndex
        lastIndex = Math.max(prevChild._mountIndex, lastIndex);
        // 更新component的.mountIndex属性
        prevChild._mountIndex = nextIndex;
      } else {
        if (prevChild) {
          // 更新lastIndex
          lastIndex = Math.max(prevChild._mountIndex, lastIndex);
        }
        // 添加新的子节点在指定的位置上
        updates = enqueue(
          updates,
          this._mountChildAtIndex(
            nextChild,
            mountImages[nextMountIndex],
            lastPlacedNode,
            nextIndex,
            transaction,
            context
          )
        );
        nextMountIndex++;
      }
      // 更新nextIndex
      nextIndex++;
      lastPlacedNode = ReactReconciler.getHostNode(nextChild);
    }
    // 移除掉不存在的旧子节点,和旧子节点和新子节点不同的旧子节点
    for (name in removedNodes) {
      if (removedNodes.hasOwnProperty(name)) {
        updates = enqueue(
          updates,
          this._unmountChild(prevChildren[name], removedNodes[name])
        );
      }
    }
  }



目录
相关文章
|
iOS开发
iOS Principle:CGAffineTransform
iOS Principle:CGAffineTransform
182 0
iOS Principle:CGAffineTransform
|
安全 Unix API
iOS Principle:CALayer(下)
iOS Principle:CALayer(下)
172 0
iOS Principle:CALayer(下)
|
iOS开发
iOS Principle:CALayer(中)
iOS Principle:CALayer(中)
151 0
iOS Principle:CALayer(中)
|
API C语言 iOS开发
iOS Principle:CALayer(上)
iOS Principle:CALayer(上)
179 0
iOS Principle:CALayer(上)
|
存储 缓存 iOS开发
iOS Principle:weak
iOS Principle:weak
169 0
iOS Principle:weak
|
存储 iOS开发
iOS Principle:Notification(下)
iOS Principle:Notification(下)
117 0
iOS Principle:Notification(下)
|
设计模式 iOS开发
iOS Principle:Notification(上)
iOS Principle:Notification(上)
132 0
iOS Principle:Notification(上)
|
Web App开发 JSON 移动开发
iOS Principle:ReactNative(下)
iOS Principle:ReactNative(下)
184 0
iOS Principle:ReactNative(下)
|
1月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
111 1
|
9天前
|
安全 数据处理 Swift
深入探索iOS开发中的Swift语言特性
本文旨在为开发者提供对Swift语言在iOS平台开发的深度理解,涵盖从基础语法到高级特性的全面分析。通过具体案例和代码示例,揭示Swift如何简化编程过程、提高代码效率,并促进iOS应用的创新。文章不仅适合初学者作为入门指南,也适合有经验的开发者深化对Swift语言的认识。
29 9