ReactNative原理與核心知識點

2023-06-21 18:00:35
React Native特點
跨平臺
使用js寫出頁面元件程式碼被React框架統一轉成Virtual DOM樹,Virtual DOM樹是UI結構的一層抽象,可以被轉換成任何支援端的UI檢視。
ReactNative框架將Virtual DOM 轉成原APP的UIView樹。
熱修復
ReactNative的產物bundle包,bundle包中包含的為RN業務所需要的所有資源,包括程式碼和資源。bundle的載入方式是APP啟動時從後臺下載,然後通過js虛擬機器器執行的。
所以可以將每次業務迭代修改後的程式碼上傳到服務,進行使用者無感知版本更新。
注意:
bundle中的業務程式碼不能修改APP現有的原生行為,不能呼叫私有API,不然禁止上架。
bundle包中的js是經過babel跳脫後的普通js,而非jsx語法糖。

 

JS與Native互動的基本原理
JS引擎
iOS側使用的JavaScriptCore作為bundle產物的js執行引擎。
JS與Native互動的基本原理很簡單,就是在JS的全域性上下文新增成員變數。原生呼叫JS是JS在JS上下文中新增方法成員變數,然後原生呼叫。JS呼叫原生是原生往JS上下文中新增方法成員變數,然後JS呼叫。
JS呼叫原生
通過將block物件賦值給js全域性上下文中的全域性變數,js內部呼叫這個全域性方法進行執行。
ctx[@"NativeMethod"] = ^(NSString *name) {
    // do something
    return someResult
}
原生呼叫JS
先建立一個JS上下文物件,在上下文中新增方法的全域性變數。原生獲取上下文的全域性變數Value, 然後呼叫,執行這個JS方法。
// 建立一個ctx的JS上下文
JSContent *ctx = [[JSContent alloc] init];
// 建立一個變數name
[ctx evaluateScript:@"var name = 'jack'"];
// 建立一個方法
[ctx evaluateScript:@"var sayHi = function(name) { return 'hello ' + name }"];


// 通過ctx上下文物件,獲取到hello方法
JSValue *sayHiUnction = ctx[@"sayHi"];
// 執行js方法
JSValue *greetings = [sayHiUnction callWithArguments:@[@"jack"]; // hello jack
ReactNative框架中原生與JS的呼叫基本思路也是這種,只不過考慮到大量的Native物件註冊會汙染js引擎中的上下文,增加了一層Bridge。
原生和JS之間的互動都是通過Bridge這個通道,通過裡面的幾個基礎方法進行互動。原生與JS的互動是非同步的。
另外,Facebook為了提升RN框架中JS的執行效率,專門推出了一個JS引擎 Hermes, 在關鍵指標上,相比於JSCore,V8提升了不少,比較易於RN框架整合。

 

ReactNative核心知識

RCTBridge: ReactNative中原生與JS互動的通道
RCTBridge用於給js引擎提供原生擴充套件介面。將原生功能如定位,3D等通過Bridge將其封裝成JS介面,然後注入到js引擎的上下文中。
RN框架啟動的簡單流程為:首先將js程式碼載入到記憶體,然後建立RCTBridge範例,然後建立RCTRootContentView內容展示的容器檢視,然後呼叫JS上下文中AppRegistry物件的runApplication方法,並將@[moduleName, appParameters]元件名,引數傳遞給JS。
// RCTRootView.m
- (void)javaScriptDidLoad:(NSNotification *)notification
{
  RCTAssertMainQueue();
  RCTBridge *bridge = notification.userInfo[@"bridge"];
  if (bridge != _contentView.bridge) {
    [self bundleFinishedLoading:bridge];
  }
}

- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
  // 省略建立RCTRootContentView...

  [self runApplication:bridge];

  // 省略新增一個RCTRootContentView...
}

- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @""; // 這裡是@"NewProject"
  NSDictionary *appParameters = @{
    @"rootTag": _contentView.reactTag,
    @"initialProps": _appProperties ?: @{},
  };

  [bridge enqueueJSCall:@"AppRegistry"                 method:@"runApplication"                   args:@[moduleName, appParameters]
             completion:NULL];
}
 
原生呼叫JS
在JS上下文中,呼叫JS的方式是通過方法:global.batchedBridge.callFunctionReturnFlushedQueue

 

所以RN在原生側的的JS引擎的封裝物件中使用成員變數儲存了這個JS的函數指標,原生呼叫JS時,通過傳遞引數 moduleid 和 methodid 完成方法的呼叫。
void JSIExecutor::bindBridge() {
  std::call_once(bindFlag_, [this] {
    SystraceSection s("JSIExecutor::bindBridge (once)");
    Value batchedBridgeValue =
        runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined() || !batchedBridgeValue.isObject()) {
      throw JSINativeException(
          "Could not get BatchedBridge, make sure your bundle is packaged correctly");
    }

    Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
    callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue");
    invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "invokeCallbackAndReturnFlushedQueue");
    flushedQueue_ =
        batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
  });
}
 
JS呼叫原生
JS呼叫原生通常是通過原生主動處理_eventQueue中的事件,特殊情況會直接呼叫原生註冊給JS的nativeFlushQueueImmediate方法, 並傳遞moduleName 、methodName、callback 引數給這個方法完成呼叫。

 

void JSIExecutor::initializeRuntime() {
  SystraceSection s("JSIExecutor::initializeRuntime");
  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));

  runtime_->global().setProperty(
      *runtime_,
      "nativeFlushQueueImmediate",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) {
            if (count != 1) {
              throw std::invalid_argument(
                  "nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));

  runtime_->global().setProperty(
      *runtime_,
      "nativeCallSyncHook",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) { return nativeCallSyncHook(args, count); }));

  runtime_->global().setProperty(
      *runtime_,
      "globalEvalWithSourceUrl",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) { return globalEvalWithSourceUrl(args, count); }));

  if (runtimeInstaller_) {
    runtimeInstaller_(*runtime_);
  }
  bool hasLogger(ReactMarker::logTaggedMarker);
  if (hasLogger) {
    ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
  }
}

Virtual DOM 虛擬DOM
 
虛擬DOM的特點
1.用於描述頁面的UI結構:在作用上虛擬DOM和普通的DOM是一樣的。
2.平臺無關性:虛擬DOM表示的UI結構是對UI的一層抽象,它是平臺無關的。具體的UI渲染是交個具體的平臺渲染引擎進行的,如iOS,安卓自身的渲染引擎。
虛擬DOM對標籤的定義
虛擬DOM把標籤分為2類:原子型標籤,組合型標籤。
原子型標籤是平臺支援型的基礎標籤,如果RCTView, RCTText。對應瀏覽器頁面中,原子型標籤有h1,li,div等。
組合型標籤是使用者自定義的元件,它在虛擬DOM中對應的是自定義標籤構造器函數,頁面渲染時呼叫這個建構函式,建立一個範例,然後呼叫範例的render方法,組合型標籤的render方法內會把組合標籤進行拆解,最後拆解成基本的原子型標籤。
var ele = {
    ...
    type: type, // 元素的型別
    key: key, // 元素key標示
    ref: ref, // 元素的參照
    props: props, // 元素的引數,包含children
    ...
}

// example 1
<div>hello</div>
// 會被描述為

{type: 'div',
    props: {
        children: ['hello']
    }
}

// example 2
<CustomerComponents />
// 會被描述為
{
    type: CustomerComponents
}
 
UI渲染
RN框架與瀏覽器的對比:在瀏覽器中,JS通過呼叫DOM API建立UI檢視。在RN中,JS通過呼叫RCTUIManager來建立iOS,Android行動端的UI檢視。
RN的UI渲染是基於虛擬DOM的,通過根據不同的平臺呼叫不同平臺的Bridge, Brideg再呼叫不同平臺的的RCTUIManager進行UI的建立。
 

其他

三條執行緒
RN內部有三條執行緒在同時執行著:Shadow Thread, JS Thread, UI Thread。
JS Thread:JS執行緒,負責JS與原生的互動,它們的互動是非同步的,每次呼叫都是將block放入佇列中,等js程式碼執行完後,讀取事件佇列進行處理。
UI Thread:UI主執行緒,負責頁面的互動與渲染, 由RCTUIManager使用。
Shadow Thread: 負責將flex佈局轉成Native的佈局,由yago引擎使用。

三個佇列
RN框架內,原生與JS的交換型別分兩種:UI和事件,這2這種事件的處理都是非同步的,它們都是將事件順序放置到佇列中,在合適的時機被呼叫。
事件的處理在RCTBridge中處理,UI的處理在RCTUIManager中處理。
JS呼叫原生非同步事件佇列:_eventQueue佇列
原生呼叫JS非同步事件佇列:_pendingCalls佇列
UI更新非同步事件處理佇列:_pendingUIBlocks佇列

JSI
javascript interface js虛擬機器器通用介面層,是針對JS引擎封裝的上層API框架,使用JSI做JS引擎呼叫的優點:
1.底部可以任意替換JS引擎而不影響上層JS引擎的使用。如:可以任意替換JavaScript Core, V8等。
2.通過JSI,JavaScript可以持有C++宿主物件的參照,所以可以直接呼叫原生方法(UIView, NativeModule),它與現在統一使用Bridge這個通道和訊息非同步呼叫比起來,提高了訊息傳送的及時性,避免了訊息佇列執行的等待。

React Native核心知識在框架中的使用
 
React Native核心功能在RN專案啟動時會進行各自的初始化,生成bundle執行上下文。在型別上可以分為2類:
1.JS與原生的事件處理:建立RCTBridge橋接通道。
2.UI互動與更新的事件處理:建立RCTRootView容器檢視。
APP啟動,React Native執行環境初始化。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  if (!self.bridge) {
    self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
  }

  NSDictionary *initProps = [self prepareInitialProps];
  UIView *rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];

  if (@available(iOS 13.0, *)) {
    rootView.backgroundColor = [UIColor systemBackgroundColor];
  } else {
    rootView.backgroundColor = [UIColor whiteColor];
  }

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [self createRootViewController];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}
 
JS與原生的事件處理:建立RCTBridge橋接通道
RCTBridge的主要邏輯是在batchedBridge中,主要初始化流程為:
1.初始化Native Modules
2.建立Native Modules設定表
3.準備JS引擎工廠,建立JS引擎
4.將Modules設定資訊註冊到JS引擎中
5.載入boundle程式碼
6.執行boundle程式碼
- (void)setUp
{
  RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);

  //_performanceLogger紀錄檔工具初始化

  //_bundleURL獲取

  //batchedBridge建立

  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  [self.batchedBridge start];

  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}

batchedBridge是RCTCXXBridge,它的初始化方法如下:
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
{
  RCTAssertParam(bridge);

  if ((self = [super initWithDelegate:bridge.delegate
                            bundleURL:bridge.bundleURL
                       moduleProvider:bridge.moduleProvider
                        launchOptions:bridge.launchOptions])) {
    _parentBridge = bridge;
    _performanceLogger = [bridge performanceLogger];

    registerPerformanceLoggerHooks(_performanceLogger);

    /**
     * Set Initial State
     */
    _valid = YES;
    _loading = YES;
    _moduleRegistryCreated = NO;
    _pendingCalls = [NSMutableArray new];
    _displayLink = [RCTDisplayLink new];
    _moduleDataByName = [NSMutableDictionary new];
    _moduleClassesByID = [NSMutableArray new];
    _moduleDataByID = [NSMutableArray new];
    _objCModuleRegistry = [RCTModuleRegistry new];
    [_objCModuleRegistry setBridge:self];
    _bundleManager = [RCTBundleManager new];
    [_bundleManager setBridge:self];
    _viewRegistry_DEPRECATED = [RCTViewRegistry new];
    [_viewRegistry_DEPRECATED setBridge:self];
    _callableJSModules = [RCTCallableJSModules new];
    [_callableJSModules setBridge:self];

    [RCTBridge setCurrentBridge:self];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleMemoryWarning)
                                                 name:UIApplicationDidReceiveMemoryWarningNotification
                                               object:nil];

    RCTLogSetBridgeModuleRegistry(_objCModuleRegistry);
    RCTLogSetBridgeCallableJSModules(_callableJSModules);
  }
  return self;
}


- (void)start
{
  RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil);

  [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
                                                      object:_parentBridge
                                                    userInfo:@{@"bridge" : self}];

  //啟動JS執行緒
  _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
  _jsThread.name = RCTJSThreadName;
  _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
  _jsThread.stackSize *= 2;
#endif
  [_jsThread start];

  dispatch_group_t prepareBridge = dispatch_group_create();

  [_performanceLogger markStartForTag:RCTPLNativeModuleInit];

  //1.初始化Native Modules
  [self registerExtraModules];
  //2.建立Native Modules設定表
  // Initialize all native modules that cannot be loaded lazily
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
  [self registerExtraLazyModules];

  [_performanceLogger markStopForTag:RCTPLNativeModuleInit];

  // This doesnt really do anything.  The real work happens in initializeBridge.
  _reactInstance.reset(new Instance);

  __weak RCTCxxBridge *weakSelf = self;

  // 3.準備JS引擎工廠,建立JS引擎
  std::shared_ptr<JSExecutorFactory> executorFactory;
  if (!self.executorClass) {
    if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
      id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>)self.delegate;
      executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
    }
    // 4.將Modules設定資訊註冊到JS引擎中
    if (!executorFactory) {
      auto installBindings = RCTJSIExecutorRuntimeInstaller(nullptr);
#if RCT_USE_HERMES
      executorFactory = std::make_shared<HermesExecutorFactory>(installBindings);
#else
      executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);
#endif
    }
  } else {
    id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
    executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
      if (error) {
        [weakSelf handleError:error];
      }
    }));
  }

    //_turboModuleRegistry是一個TurboModule登入檔,TurboModule是JS在RN中的一種優化方式,將常用的JS程式碼編譯成可執行程式碼,提高執行速度。
  /**
   * id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleRegistry> object to
   * RCTCxxBridge If id<RCTTurboModuleRegistry> is assigned by this time, eagerly initialize all TurboModules
   */
  if (_turboModuleRegistry && RCTTurboModuleEagerInitEnabled()) {
    for (NSString *moduleName in [_turboModuleRegistry eagerInitModuleNames]) {
      [_turboModuleRegistry moduleForName:[moduleName UTF8String]];
    }

    for (NSString *moduleName in [_turboModuleRegistry eagerInitMainQueueModuleNames]) {
      if (RCTIsMainQueue()) {
        [_turboModuleRegistry moduleForName:[moduleName UTF8String]];
      } else {
        id<RCTTurboModuleRegistry> turboModuleRegistry = _turboModuleRegistry;
        dispatch_group_async(prepareBridge, dispatch_get_main_queue(), ^{
          [turboModuleRegistry moduleForName:[moduleName UTF8String]];
        });
      }
    }
  }

  // Dispatch the instance initialization as soon as the initial module metadata has
  // been collected (see initModules)
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];

  // 5.載入boundle程式碼
  // Load the source asynchronously, then store it for later execution.
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self
      loadSource:^(NSError *error, RCTSource *source) {
        if (error) {
          [weakSelf handleError:error];
        }

        sourceCode = source.data;
        dispatch_group_leave(prepareBridge);
      }
      onProgress:^(RCTLoadingProgress *progressData) { }];

  // 模組和js程式碼載入完成後,執行js程式碼
  // Wait for both the modules and source code to have finished loading
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      // 6.執行boundle程式碼
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
 
初始化Native Modules與建立Native Modules設定表
把原生的RN模組都收集起來,包括RN框架自帶的和使用者自定義的,將模組資訊儲存到Bridge的變數中,用於與JS交換。
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
 
JS傳送訊息到Native時,通過- (id)moduleForName:(const char *)moduleName;查詢到模組詳情,進行模組呼叫。
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];

準備JS引擎工廠,建立JS引擎與將Modules設定資訊註冊到JS引擎中
RN將Native Modules資訊收集完成後儲存到成員變數中,這個成員變數是一個陣列。使用moduleConfig儲存模組的模組名,方法名。然後將這些資料注入到JS引擎中。
JS呼叫原生時,通過模組名,方法名,引數呼叫原生方法。在原生呼叫JS時,會將呼叫放入_pendingCalls佇列中,進行非同步執行。而JS調原生是將呼叫放入到_eventQueue佇列中,進行非同步執行。
JS可以通過方法nativeFlushQueueImmediate直接呼叫Native,但是一般JS不會這樣做,而是等原生自己去_eventQueue佇列中自己去取任務做處理。
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
- (void)flushEventsQueue
{
  [_eventQueueLock lock];
  NSDictionary *events = _events;
  _events = [NSMutableDictionary new];
  NSMutableArray *eventQueue = _eventQueue;
  _eventQueue = [NSMutableArray new];
  _eventsDispatchScheduled = NO;
  [_eventQueueLock unlock];

  for (NSNumber *eventId in eventQueue) {
    [self dispatchEvent:events[eventId]];
  }
}
 
UI互動與更新的事件處理:建立RCTRootView容器檢視
 
RCTRootView為RN頁面的入口,在RCTRootView初始化過程中,會建立RCTRootContentView作為內容檢視放置在RCTRootView的底部作為根檢視。
RCTRootContentView的初始化方法中,在uiManager中將RCTRootContentView註冊成根檢視。
- (instancetype)initWithFrame:(CGRect)frame
                       bridge:(RCTBridge *)bridge
                     reactTag:(NSNumber *)reactTag
               sizeFlexiblity:(RCTRootViewSizeFlexibility)sizeFlexibility
{
  if ((self = [super initWithFrame:frame])) {
    _bridge = bridge;
    self.reactTag = reactTag;
    _sizeFlexibility = sizeFlexibility;
    _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
    [_touchHandler attachToView:self];
    [_bridge.uiManager registerRootView:self];
  }
  return self;
}
RCTUIManager是RN中UI的管理者,它負責處理所有與UI相關的事情,如:注入到JS中的建立View方法。
在createView方法中可以看到,RN對View的操作都是雙份的,分別作用在RCTShadowView和UIView上。RCTShadowView和UIView的關係類似於虛擬DOM和DOM的關係。
RCTShadowView是一個虛擬DOM樹,是一個結構體,用於描述檢視的樣式和事件,比較輕量級。
在RN中當呼叫setState更新元件狀態時,就會生成一個新的虛擬DOM,然後RN將新的虛擬DOM與舊的虛擬DOM進行Diff對比,生成差異物件,然後遍歷差異物件,將所有的改動更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合適的時候再統一更新到UI上。

UI更新操作也是非同步的,更新任務被放置在_pendingUIBlocks佇列上,在UI變化時或Bridge批出來結束時重新整理這個佇列。
RCT_EXPORT_METHOD(createView
                  : (nonnull NSNumber *)reactTag viewName
                  : (NSString *)viewName rootTag
                  : (nonnull NSNumber *)rootTag props
                  : (NSDictionary *)props)
{
  RCTComponentData *componentData = _componentDataByName[viewName];
  if (componentData == nil) {
    RCTLogError(@"No component found for view with name \"%@\"", viewName);
  }

  // Register shadow view
  RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
  if (shadowView) {
    [componentData setProps:props forShadowView:shadowView];
    _shadowViewRegistry[reactTag] = shadowView;
    RCTShadowView *rootView = _shadowViewRegistry[rootTag];
    RCTAssert(
        [rootView isKindOfClass:[RCTRootShadowView class]] || [rootView isKindOfClass:[RCTSurfaceRootShadowView class]],
        @"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.",
        rootTag);
    shadowView.rootView = (RCTRootShadowView *)rootView;
  }

  // Dispatch view creation directly to the main thread instead of adding to
  // UIBlocks array. This way, it doesnt get deferred until after layout.
  __block UIView *preliminaryCreatedView = nil;

  void (^createViewBlock)(void) = ^{
    // Do nothing on the second run.
    if (preliminaryCreatedView) {
      return;
    }

    preliminaryCreatedView = [componentData createViewWithTag:reactTag rootTag:rootTag];

    if (preliminaryCreatedView) {
      self->_viewRegistry[reactTag] = preliminaryCreatedView;
    }
  };

  // We cannot guarantee that asynchronously scheduled block will be executed
  // *before* a block is added to the regular mounting process (simply because
  // mounting process can be managed externally while the main queue is
  // locked).
  // So, we positively dispatch it asynchronously and double check inside
  // the regular mounting block.

  RCTExecuteOnMainQueue(createViewBlock);

  [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
    createViewBlock();

    if (preliminaryCreatedView) {
      [componentData setProps:props forView:preliminaryCreatedView];
    }
  }];

  [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
}

 



參考文章
https://juejin.cn/post/6916452544956858382#heading-11
https://juejin.cn/post/6844904184542822408
https://juejin.cn/post/6844904184500715527