ReactNative的View
RCTRootView
RCTRootView初始化流程
创建传入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