ReactNative的View

RCTRootView

RCTRootView初始化流程

Alt text

创建传入bridge和modulename,以及initialProperties

  • modulename是创建bridge的时候会调用js的AppRegistry的runApplication方法传入modulename
  • initialProperties通过runApplication传入JS,这里可以传入路由页面的配置,runApplication之后会调用renderApplication,然后开始调用组件的渲染

RCTUIManager

RCTUIManager实现了RCTBridgeModule,其methodQueue是shadow queue

registerRootView

  • rootView保存在_viewRegistry map
  • 初始化RCTRootShadowView,保存到_shadowViewRegistry

addUIBlock

保存UI block到_pendingUIBlocks

flushUIBlocksWithCompletion,从shadow queue 切换到main queue 进行mountingBlock调用。这里主要是遍历_pendingUIBlocks,调用其元素block

batchDidComplete,这里调用_layoutAndMount,主要是触发layout和mount操作,整个操作是被派发在shadow queue操作的

补充一句,所有实现batchDidComplete的module都会被RCTCxxBridge调用在对应的methodQueue,batchDidComplete是在callNativeModules的每一次调用结束后调用的。

_layoutAndMount的流程

  • _dispatchPropsDidChangeEvents,派发属性已发生改变事件,这里会分别对shadow view和view调用didSetProps方法
  • _dispatchChildrenDidChangeEvents,派发子view已发生改变事件
  • uiManagerWillPerformLayout通知,即将布局
  • _rootViewTags遍历,据此取出RCTRootShadowView,并且调用[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]]
  • uiManagerDidPerformLayout通知,布局完成
  • uiManagerWillPerformMounting通知,即将安装
  • flushUIBlocksWithCompletion,completion里发出安装完成的通知

uiBlockWithLayoutUpdateForRootView布局的过程如下

  • 对传入rootShadowView的调用layoutWithAffectedShadowViews
  • 遍历上一个函数返回的affectedShadowViews,首先构造reactTags数组,元素为shadowView.reactTag,其次构造frameDataArray(RCTFrameData)数组,数据从affectedShadowViews元素的RCTShadowView来
  • 再次遍历affectedShadowViews,调用元素RCTShadowView的onLayout方法
  • 返回callback
    • 遍历reactTags
    • 通过viewRegistry[reactTag]取出UIView
    • 若creatingLayoutAnimation,先调用reactSetFrame,然后通过performAnimations调用transform/opacity动画
    • 若updatingLayoutAnimation,通过performAnimations来调用reactSetFrame
    • 其他情况调用view的reactSetFrame(frameData.frame)

bridge method

createView,创建RCTShadowView、RCTView并赋值给RCTViewManager

updateView

setChildren

  • RCTSetChildren调用,传入_shadowViewRegistry
    • RCTSetChildren三个参数,containerTag(父view),reactTags(子views),registry(shadowViewRegistry/viewRegistry)
    • 通过registry和containerTag取出对应的container
    • 依次遍历reactTags,调用container的insertReactSubview
  • 通过addUIBlock进行(这会在main queue)RCTSetChildren调用,但传入的是viewRegistry
  • _shadowViewsWithUpdatedChildren增加_shadowViewRegistry[containerTag]

measure方法,在main queue调用

  • 从viewRegistry[reactTag]取出对应UIView
  • 找到传入view的ReactRootView(遍历superView,判断标准reactTag.integerValue % 10 == 1)
  • 根据view和root view 转换为对应的坐标
  • 返回响应的位置和大小

RCTViewManager

RCTViewManager管理着RCTShadowView和对应的RCTView。

在RCTUIManager里的createView方法里面,会创建RCTShadowView、RCTView并赋值给RCTViewManager

RCTViewManager的结构图

RCTView

pointerEvents

js侧的View组件可以通过该属性控制当前视图是否可以作为触控事件的目标。

  • auto:视图可以作为触控事件的目标。
  • none:视图不能作为触控事件的目标。
  • box-none:视图自身不能作为触控事件的目标,但其子视图可以。
  • box-only:视图自身可以作为触控事件的目标,但其子视图不能。

enum对应native的转换如下

RCT_ENUM_CONVERTER(
    RCTPointerEvents,
    (@{
      @"none" : @(RCTPointerEventsNone),
      @"box-only" : @(RCTPointerEventsBoxOnly),
      @"box-none" : @(RCTPointerEventsBoxNone),
      @"auto" : @(RCTPointerEventsUnspecified)
    }),
    RCTPointerEventsUnspecified,
    integerValue)

具体实现见hitTest

  • 若RCTPointerEventsNone则hitTest返回nil,
  • 若RCTPointerEventsBoxOnly则hitTest返回hitView,hitView即self(若事件点在self内),
  • 若RCTPointerEventsBoxNone则hitTest返回hitSubView,该hitSubView的查找规则为遍历sub view,调用sub view的hitTest,命中则跳出循环
  • RCTPointerEventsUnspecified为默认值,hitSubview有则hitSubview,否则hitView

displayLayer流程

  • 若useIOSBorderRendering为yes,则直接设置传入layer的border、coloer等
  • 若为no则
    • 调用RCTGetBorderImage获取UIImage对象,传入的有border style,size,corner radius,color 等等
    • 把image设置为layer的contents

      RCTShadowView

insertReactSubview

  • 插入传入的subview(shadow view)到_reactSubviews数组的指定index
  • 如果非叶子节点,调用YGNodeInsertChild
  • subview的superview设置为self

layoutWithMinimumSize 布局方法

  • layoutWithMetrics
  • layoutSubviewsWithContext,会递归调用layoutSubviewsWithContext获取到对应layout metrics

RCTTouchHandler

RCTTouchHandler接收事件流程

RCTComponent

RCTComponent协议为组件的逻辑节点,ShadowView和UIView都会遵循这个协议

主要包含以下方法

  • insert/remove/subviews/superview等方法
  • didSetProps方法,属性设置完成后调用,这个由具体的业务组件负责实现,以Image为例子,这里会调用reloadImage方法
  • didUpdateReactSubviews,子view更新时调用

RCTComponentData

RCTUIManager的_componentDataByName保存着组件名和RCTComponentData的映射

初始化,在RCTUIManagaer设置bridge的时候进行初始化

RCTComponentData的几个主要方法

  • createView,createShadowView方法
  • setProps,遍历属性列表调用propBlockForKey,这里主要是找到RCTPropBlock,并且执行,如果没有RCTPropBlock则会创建,这里实现为createPropBlock,主要是执行对应的属性的setter方法
  • viewConfig,返回propTypes、directEvents、bubblingEvents、baseModuleName
    • propTypes,遍历对应ViewManager的方法,找到RCT_EXPORT_VIEW_PROPERTY包装的属性
    • directEvents
    • bubblingEvents,对应属性的类型为RCTBubblingEventBlock,可多次调用,比如Switch组件定义的onChange属性
    • baseModuleName,对应属性的类型为RCTDirectEventBlock,可多次调用,比如Image组件的onProgress,onLoadStart,onLoadEnd等

关于属性调用,以Image组件的source为例

  • RCTUIManager flushUIBlocks触发
  • RCTComponentData setProps
  • 取出对应的propBlock
  • RCTImageView setImageSources

results matching ""

    No results matching ""