• APP中RN页面渲染流程-ReactNative源码分析


    在APP启动后,RN框架开始启动。等RN框架启动后,就开始进行RN页面渲染了。
    RN页面原生侧页面渲染的主要逻辑实现是在RCTUIManager和RCTShadowView完成的。
    通过看UIMananger的源码可以看到,UIMananger导出给JS端的API接口在对UI的操作上,基本都会同时对 View 和 ShadowView 进行操作。
    以更新视图为例:


    RCTUIManager的作用
    RCTUIManager的主要作用是负责管理React Native应用程序的视图的创建、更新和销毁。
    具体为RCTUIManager负责创建并管理应用程序中的所有视图,包括文本、图像、按钮等。当需要更新或销毁视图时,RCTUIManager会负责处理相应的操作。
     
     
    RCTShadowView的作用 
    RCTShadowView的主要作用是在APP中创建一棵YaGoNode节点树,用于记录视图的样式、布局、事件响应等信息,用于描述真实视图的属性和布局,从而提高渲染性能和效率。,它和UIView的关系类似于前端的虚拟DOM树和DOM树,两者是一一对应的关系。js对View的操作会先更新虚拟DOM,然后ReactNative在合适的时机批量更新到真实的View上。

    RN页面的创建
    在创建自定义RCTView供js使用时,一般在创建一个RCTView时,都要创建一个对应的RCTViewManager用于管理Native与js的通讯。
    UIMananger的创建在RN框架启动时,它在创建时会通过RCT_EXPORT_METHOD()宏将操作view的添加,修改,删除,调整层级等方法注入给js,供js操作原生view
    RN框架启动完成后则会进行RN页面渲染。
     
    首先,js引擎执行rn代码,将rn中的组件转换成原生view展示到页面上。
    js引擎执行js代码, 它会根据RN页面中的标签类型转换成一棵虚拟DOM树,它们有原子组件,文本组件,组合组件组成。在这些组件的内部记录着create视图需要的参数。等要生成真实的视图时,就会调用指令生成对应的视图。

    原生侧createView方法的主要执行步骤为:

    1
    2
    3
    4
    5
    RCT_EXPORT_METHOD(createView
    : (nonnull NSNumber *)reactTag viewName
    : (NSString *)viewName rootTag
    : (nonnull NSNumber *)rootTag props
    : (NSDictionary *)props)
    1.根据模块名viewName从RCTBridge保存的全局变量中找到对应的模块信息
    2.根据模块信息创建shadowview虚拟dom,保存到shadowView全局容器中
    3.根据模块信息在主线程创建原生view,保存到view全局容器中
     
    然后,执行setChildren:设置子视图
    执行setChildren:设置子视图, 会将view添加到容器view的reactSubviews中(shadowView和UIView都是放到对应容器的reactSubviews属性中)[container insertReactSubview:view atIndex:index++];

    原生侧setChildren方法的主要执行步骤为:

    1
    RCT_EXPORT_METHOD(setChildren : (nonnull NSNumber *)containerTag reactTags : (NSArray *)reactTags)
    1.设置shadowView子视图,shadowView是设置到yoga树的叶子节点中:YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
    2.把设置view子视图任务添加到任务队列,[_pendingUIBlocks addObject:block];队列中的任务并不会立刻执行,而是等到合适的时机再执行。而当这个任务执行后,子View也并没有到真实的subviews中,而是放置到了reactSubviews关联属性中 objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     
    _pendingUIBlocks队列执行时机
    _pendingUIBlocks队列的刷新时机主要有2种,一种是在JS代码执行完成,JS通过RCTCxxBridge调用batchDidComplete触发刷新。另一种在原生APP中根据RN页面的布局手动调用setNeedsLayout进行触发。
    JS通过RCTCxxBridge调用
    在js执行期间,js引擎通过Bridge桥接,把涉及到UI操作的事件按顺序封装成UIBlock放到Native原生侧的_pendingUIBlocks中,在等js代码执行完成后,原生模块会触发一个UIManager.batchDidComplete事件,表示js批量任务执行完成,开始刷新_pendingUIBlocks队列中的UI任务了。因此,在 JavaScript 执行完成前,RN 页面的 UI 并不会立即刷新。
    方法调用顺序:batchDidComplete -> _layoutAndMount -> flushUIBlocksWithCompletion。
    _pendingUIBlocks中的UIBlock执行后,最终会生成真实的原生view
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct RCTInstanceCallback : public InstanceCallback {
      __weak RCTCxxBridge *bridge_;
      RCTInstanceCallback(RCTCxxBridge *bridge) : bridge_(bridge){};
      void onBatchComplete() override
      {
        // There's no interface to call this per partial batch
        [bridge_ partialBatchDidFlush];
        [bridge_ batchDidComplete];
      }
    };
    RN页面根据页面布局手动调用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - (void)setSize:(CGSize)size forView:(UIView *)view
    {
      RCTAssertMainQueue();
      [self
          _executeBlockWithShadowView:^(RCTShadowView *shadowView) {
            if (CGSizeEqualToSize(size, shadowView.size)) {
              return;
            }
     
            shadowView.size = size;
            [self setNeedsLayout];
          }
                               forTag:view.reactTag];
    }
    1
    2
    3
    4
    5
    6
    7
    8
    - (void)setNeedsLayout
    {
      // If there is an active batch layout will happen when batch finished, so we will wait for that.
      // Otherwise we immediately trigger layout.
      if (![_bridge isBatchActive] && ![_bridge isLoading]) {
        [self _layoutAndMount];
      }
    }
    RN页面更新
    当组件调用了setState属性更新时,通过updateView:刷新视图。
    当出现插入、删除、排序组件时,通过manageChildren:更新视图。
    updateView:刷新视图
    当在RN中通过setState更改属性,js会对应生成一个新的虚拟DOM,通过diff算法,对应新旧DOM树生成修改点,然后通过updateView事件,将属性更新更新到原生侧的shadowView和View的_UIPendingQueue中。
     
    当出现插入、删除、排序组件时,通过manageChildren:更新视图
    containerTag:表示容器组件的标识符,即将在其中管理子组件。
    moveFromIndices和moveToIndices:表示要移动的子组件的原始位置和目标位置的索引。
    addChildReactTags和addAtIndices:表示要添加的子组件的标识符和它们在父容器中的位置索引。
    removeAtIndices:表示要从父容器中删除的子组件的位置索引。
    registry:表示React组件的注册表,其中包含所有已注册的组件及其实例。

     

     
     
     
     
  • 相关阅读:
    智慧城市的发展趋势
    使用极狐GitLab的VS Code插件 GitLab Workflow 来进行项目的DevOps管理,助力研发效能
    【Anaconda】conda阿里云镜像源配置
    2022!影响百万用户金融信用评分,Equifax被告上法庭,罪魁祸首——『数据漂移』!⛵
    Django常见面试题总结(二)
    VMWARE 服务器整合为战提供的解决方案
    东莞3d可视化建模,数字孪生智慧工厂3D模型开发,智慧城市园区三维模型
    altera系列fifo和ram
    仪器设备的外观设计有多重要?看完你就知道了
    “Python+”集成技术高光谱遥感数据处理
  • 原文地址:https://www.cnblogs.com/zhou--fei/p/17429837.html