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

APP,页面,流程,源码 · 浏览次数 : 159

小编点评

**RN页面渲染过程** 1. **JS引擎执行RN代码,将rn中的组件转换成原生view展示到页面上。** 2. **JS引擎执行js代码,它会根据RN页面中的标签类型转换成一棵虚拟DOM树,它们有原子组件、文本组件、组合组件组成。** 3. **在这些组件的内部记录着create视图所需的参数。** 4. **等要生成真实的视图时,就会调用指令生成对应的视图。** 5. **原生侧createView方法的主要执行步骤为:** - **创建shadowview虚拟dom,保存到shadowView全局容器中。** - **根据模块信息在主线程创建原生view,保存到view全局容器中。** 6. **执行setChildren:设置子视图执行setChildren:设置子视图, 会将view添加到容器view的reactSubviews中。** 7. **在js执行完成前,RN 页面的 UI 并不会立即刷新。** 8. **方法调用顺序:** - **batchDidComplete -> _layoutAndMount -> flushUIBlocksWithCompletion。** 9. **_pendingUIBlocks中的UIBlock执行后,最终会生成真实的原生viewstruct RCTInstanceCallback。** 10. **RN页面根据页面布局手动调用- (void)setSize:(CGSize)size forView:(UIView *)view。** 11. **当出现插入、删除、排序组件时,通过manageChildren:更新视图。** 12. **updateView:刷新视图当在RN中通过setState更改属性,js会对应生成一个新的虚拟DOM,通过diff算法,对应新旧DOM树生成修改点,然后通过updateView事件,将属性更新更新更新到原生侧的shadowView和View的_UIPendingQueue中。

正文

在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方法的主要执行步骤为:

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方法的主要执行步骤为:

RCT_EXPORT_METHOD(setChildren : (nonnull NSNumber *)containerTag reactTags : (NSArray<NSNumber *> *)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
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页面根据页面布局手动调用
- (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];
}
- (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组件的注册表,其中包含所有已注册的组件及其实例。

 

 
 
 
 

与APP中RN页面渲染流程-ReactNative源码分析相似的内容:

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

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

APP中RN页面热更新流程-ReactNative源码分析

平时使用WebStorm或VSCode对RN工程中的文件修改后,在键盘上按一下快捷cmd+s进行文件保存,此时当前调试的RN页面就会自动进行刷新,这是RN开发相比于原生开发一个很大的优点:热更新。 那么,从按一下快捷cmd+s到RN页面展示出最新的JS页面,这个过程是怎样发生的呢?下面根据时间顺序来

APP中Web容器的核心实现

现在的业务型APP中,采用纯原生开发策略的已经很少了,大部分都使用的混合开发。如原生,H5,ReactNative,Flutter,Weex它们之间任意的组合就构成了混合开发。 其中原生+H5是出现最早的,老牌混合方案,即使过来多年,在现在的混合开发方案中H5也是使用率非常高的。在APP中嵌入Web

在原生APP中集成Unity容器

随着技术的发展,越来越多的APP期望拥有3D,AR的能力。要达到这个目标可以选择使用原生开发,也可以使用Unity成熟的3D开发技术链,通过嵌入的方式将Unity容器嵌入到APP中。这里介绍的是通过嵌入Unity容器的方式来实现APP的3D,AR能力的。 Unity集成到iOS应用的本质是将Unit

flutter系列之:在flutter中使用相机拍摄照片

简介 在app中使用相机肯定是再平常不过的一项事情了,相机肯定涉及到了底层原生代码的调用,那么在flutter中如何快速简单的使用上相机的功能呢? 一起来看看吧。 使用相机前的准备工作 flutter中为使用camera提供了一个叫做camera的插件,我们首先需要安装这个插件。 安装插件的步骤很简

【Azure Logic App】在Logic App中使用 Transfer XML组件遇见错误 undefined

问题描述 在Azure Logic App中,使用Transform XML组件进行XML内容的转换,但是最近这个组件运行始终失败。 问题解答 点击Transform XML组件上的错误案例,并不能查看到详细的错误消息。 最后在Azure Logic App的产品团队确认下,发现这是Logic Ap

【Azure Function App】在ADF(Azure Data Factory)中调用 Azure Function 时候遇见 Failed to get MI access token

问题描述 在ADF(Azure Data Factory)中,调用Azure Function App中的Function,遇见了 Failed to get MI access token There was an error while calling endpoint with error m

鲸鸿动能广告接入如何高效变现流量?

广告是App开发者最常用的流量变现方法之一,当App拥有一定数量用户时,开发者就需要考虑如何进行流量变现,帮助App实现商业可持续增长。 鲸鸿动能流量变现服务是广告服务依托华为终端强大的平台与数据能力为开发者提供的App流量变现服务,开发者通过该服务可以在自己的App中获取并向用户展示精美的、高价值

Swift之struct二进制大小分析

随着Swift的日渐成熟和给开发过程带来的便利性及安全性,京喜App中的原生业务模块和基础模块使用Swift开发占比逐渐增高。本次讨论的是struct对比Class的一些优劣势,重点分析对包体积带来的影响及规避措施。

2024盘古石取证比赛(IPA+人工智能)

前言 题目列表 IPA部分 1. 分析毛雪柳的手机检材,记账 APP 存储记账信息的数据库文件名称是: [ 答案格式:tmp.db ,区分大小写 ][ ★★★★☆ ] 通过icost软件可以定位raealm数据库 default.realm 2. 分析毛雪柳的手机检材,记账APP中,2月份总收入金额