我们知道JS主要管理的是界面渲染逻辑和事件处理逻辑,那么渲染是怎么同步到Native端的呢?初始又是怎么创建的呢?
RCTRootView是入口,RCTRootView是RN的根视图,内部持有了一个RCTBridge,但是这个RCTBridge并没有太多的代码,而是持有了另一个RCTBatchBridge对象,大部分的业务逻辑都转发给BatchBridge,BatchBridge里面写着的大量的核心代码
RCTUIManager是管理所有UI的对象,负责创建,更新UIView对象。这样就借助了 iOS本身的UIView 渲染机制进行渲染。
RCTUIManager又是通过上篇所提到的通信机制作为一个JS对象来操作的。
在JS里面有一个所有JSComponent的tag表,在OC里面依然也有这么一个所有nativeView的Tag表_shadowViewRegistry,只有通过唯一指定的tag,这样RCTUIManager,才能知道到底应该操作哪一个nativeView
一、创建rootview
在开始正式创建RCTRootView的时候会创建一个subviewRCTContentRootView这个东西创建的时候需要一个reactTag,这个tag是一个很关键的东西,此时通过allocateRootTag方法创建了root得reactTag。
- (NSNumber *)reactTag
{
RCTAssertMainQueue();
if (!super.reactTag) {
/**
* Every root view that is created must have a unique react tag.
* Numbering of these tags goes from 1, 11, 21, 31, etc
*
* NOTE: Since the bridge persists, the RootViews might be reused, so the
* react tag must be re-assigned every time a new UIManager is created.
*/
self.reactTag = [_bridge.uiManager allocateRootTag];
}
return super.reactTag;
}
从注释可以看出规则是从1开始,每次创建一个RootView实例都会累加10,如1,11,21,31,以此类推。创建完RCTContentRootView后还要去UIManager用这个reactTag注册View,也就是以Tag为Key,登记进入_viewRegistry字典表,同时创建对应的shadow view。
- (void)registerRootView:(RCTRootContentView *)rootView
{
NSNumber *reactTag = rootView.reactTag;
UIView *existingView = _viewRegistry[reactTag];
CGSize availableSize = rootView.availableSize;
_viewRegistry[reactTag] = rootView;
dispatch_async(RCTGetUIManagerQueue(), ^{
if (!self->_viewRegistry) {
return;
}
RCTRootShadowView *shadowView = [RCTRootShadowView new];
shadowView.availableSize = availableSize;
shadowView.reactTag = reactTag;
shadowView.backgroundColor = rootView.backgroundColor;
shadowView.viewName = NSStringFromClass([rootView class]);
self->_shadowViewRegistry[shadowView.reactTag] = shadowView;
[self->_rootViewTags addObject:reactTag];
});
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerDidRegisterRootViewNotification
object:self
userInfo:@{RCTUIManagerRootViewKey: rootView}];
}
关于 shadow view,后面会有更详细的介绍(如果有后面的话_),简而言之就是优化布局用的。
/**
* ShadowView tree mirrors RCT view tree. Every node is highly stateful.
* 1. A node is in one of three lifecycles: uninitialized, computed, dirtied.
* 1. RCTBridge may call any of the padding/margin/width/height/top/left setters. A setter would dirty
* the node and all of its ancestors.
* 2. At the end of each Bridge transaction, we call collectUpdatedFrames:widthConstraint:heightConstraint
* at the root node to recursively lay out the entire hierarchy.
* 3. If a node is "computed" and the constraint passed from above is identical to the constraint used to
* perform the last computation, we skip laying out the subtree entirely.
*/
然后将RCTContentRootView添加到RCTRootView上面,然后执行了一行JS代码,告诉JS你要开始绘制这个参数params的界面
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
};
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
再往后就是React.JS的工作了,React.JS会着手把JS中的页面进行计算,排版,生成对应的JS Component,准备组织绘制界面了,包含着无数个JS Component的相互嵌套。最终通过UIManager.js接口,开始call oc去创建界面。
js 中如何处理 tag 的呢?
会跳过哪些 native 保留的 tag,也就是对10取余为1的那些 tag。然后自增。
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
* collisions, but in the JS we keep track of them as positive integers to store
* them effectively in Arrays. So we must refer to them as "inverses" of the
* native tags (that are * normally negative).
*
* It *must* be the case that every `rootNodeID` always maps to the exact same
* `tag` forever. The easiest way to accomplish this is to never delete
* anything from this table.
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
var INITIAL_TAG_COUNT = 1;
var ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
var tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
assertRootTag: function(tag: number): void {
invariant(
this.reactTagIsNativeTopRootID(tag),
'Expect a native root tag, instead got %s',
tag,
);
},
reactTagIsNativeTopRootID: function(reactTag: number): boolean {
// We reserve all tags that are 1 mod 10 for native root views
return reactTag % 10 === 1;
},
};
实际跑一下,创建的第一个 RootView
self->_shadowViewRegistry:
{
17 = "<RCTShadowView: 0x121ed46e0; viewName: RCTView; reactTag: 17; frame: {{0, 0}, {0, 0}}>";
15 = "<RCTShadowText: 0x123940ff0; viewName: RCTText; reactTag: 15; frame: {{0, 0}, {258.66666666666669, 37.666666666666664}}; text: Tap me to load the next scene>";
13 = "<RCTShadowView: 0x123940690; viewName: RCTView; reactTag: 13; frame: {{77.666666666666671, 357.66666666666669}, {258.66666666666669, 37.666666666666664}}>";
9 = "<RCTShadowText: 0x12393f670; viewName: RCTText; reactTag: 9; frame: {{0, 0}, {132.33333333333334, 17}}; text: Current Scene: haha>";
7 = "<RCTShadowView: 0x12393f2b0; viewName: RCTView; reactTag: 7; frame: {{0, 0}, {414, 736}}>";
5 = "<RCTShadowView: 0x12393cf60; viewName: RCTNavigator; reactTag: 5; frame: {{0, 0}, {414, 736}}>";
3 = "<RCTShadowView: 0x121e17f30; viewName: RCTView; reactTag: 3; frame: {{0, 0}, {414, 736}}>";
1 = "<RCTRootShadowView: 0x121ec5cf0; viewName: RCTRootContentView; reactTag: 1; frame: {{0, 0}, {414, 736}}>";
16 = "<RCTShadowRawText: 0x121ed3ee0; viewName: RCTRawText; reactTag: 16; frame: {{0, 0}, {nan, nan}}; text: Tap me to load the next scene>";
14 = "<RCTShadowView: 0x123940eb0; viewName: RCTView; reactTag: 14; frame: {{0, 0}, {258.66666666666669, 37.666666666666664}}>";
12 = "<RCTShadowRawText: 0x12393fc00; viewName: RCTRawText; reactTag: 12; frame: {{0, 0}, {nan, nan}}; text: haha>";
10 = "<RCTShadowRawText: 0x12393f8b0; viewName: RCTRawText; reactTag: 10; frame: {{0, 0}, {nan, nan}}; text: Current Scene: >";
8 = "<RCTShadowView: 0x121ecf2d0; viewName: RCTView; reactTag: 8; frame: {{141, 340.66666666666669}, {132.33333333333334, 17}}>";
6 = "<RCTShadowView: 0x121ec73b0; viewName: RCTNavItem; reactTag: 6; frame: {{0, 0}, {414, 736}}>";
4 = "<RCTShadowView: 0x12393ce20; viewName: RCTView; reactTag: 4; frame: {{0, 0}, {414, 736}}>";
2 = "<RCTShadowView: 0x12393cce0; viewName: RCTView; reactTag: 2; frame: {{0, 0}, {414, 736}}>";
}
创建了多个 RootView 以后
{
410 = "<RCTShadowRawText: 0x1014d0af0; viewName: RCTRawText; reactTag: 410; frame: {{0, 0}, {nan, nan}}; text: Tap me to load the next scene>";
399 = "<RCTShadowView: 0x1016812f0; viewName: RCTNavigator; reactTag: 399; frame: {{0, 0}, {414, 736}}>";
407 = "<RCTShadowView: 0x10169bc10; viewName: RCTView; reactTag: 407; frame: {{77.666666666666671, 357.66666666666669}, {258.66666666666669, 37.666666666666664}}>";
396 = "<RCTShadowView: 0x101425af0; viewName: RCTView; reactTag: 396; frame: {{0, 0}, {414, 736}}>";
221 = "<RCTRootShadowView: 0x101487710; viewName: RCTRootContentView; reactTag: 221; frame: {{0, 0}, {414, 736}}>";
404 = "<RCTShadowText: 0x1016281d0; viewName: RCTText; reactTag: 404; frame: {{0, 0}, {132.33333333333334, 17}}; text: Current Scene: haha>";
412 = "<RCTShadowView: 0x101405e40; viewName: RCTView; reactTag: 412; frame: {{0, 0}, {0, 0}}>";
409 = "<RCTShadowText: 0x10148cf10; viewName: RCTText; reactTag: 409; frame: {{0, 0}, {258.66666666666669, 37.666666666666664}}; text: Tap me to load the next scene>";
398 = "<RCTShadowView: 0x1014069b0; viewName: RCTView; reactTag: 398; frame: {{0, 0}, {414, 736}}>";
406 = "<RCTShadowRawText: 0x10169c840; viewName: RCTRawText; reactTag: 406; frame: {{0, 0}, {nan, nan}}; text: haha>";
403 = "<RCTShadowView: 0x101441830; viewName: RCTView; reactTag: 403; frame: {{141, 340.66666666666669}, {132.33333333333334, 17}}>";
400 = "<RCTShadowView: 0x101677f00; viewName: RCTNavItem; reactTag: 400; frame: {{0, 0}, {414, 736}}>";
408 = "<RCTShadowView: 0x1014c9b60; viewName: RCTView; reactTag: 408; frame: {{0, 0}, {258.66666666666669, 37.666666666666664}}>";
397 = "<RCTShadowView: 0x10146c180; viewName: RCTView; reactTag: 397; frame: {{0, 0}, {414, 736}}>";
405 = "<RCTShadowRawText: 0x1016820d0; viewName: RCTRawText; reactTag: 405; frame: {{0, 0}, {nan, nan}}; text: Current Scene: >";
402 = "<RCTShadowView: 0x10e41c750; viewName: RCTView; reactTag: 402; frame: {{0, 0}, {414, 736}}>";
}
那么RCTUIManager都有哪些API提供给了JS呢,大致如下:
createView
updateView
setChildren
removeRootView
manageChildren
findSubviewIn
measure
dispatchViewManagerCommand
createView的作用是创建一个个的UIView,RCTView,各种nativeView,并且把传过来的JS的属性参数,一一赋值给nativeView
updateView的作用是,当JSComponent的布局信息,界面样子发生变化,JS来通知nativeView来更新对应的属性变化,样子变化
setChildren的作用是,告诉OC,那个tag的View是另一个tag的view的子view,需要执行addsubview,insertsubview等
如果要创建一个 view,会经过以下流程:
js传来viewName,通过初始化的_componentDataByName表获取RCTComponentData
dispatch_async(mainqueue)从JS通信线程抛到主线程创建UI
js传来了ReactTag,通过RCTComponentData的createViewWithTag方法创建界面
js传来了属性props,通过RCTComponentData的setProps:forView:方法进行属性赋值
mountComponent: function mountComponent(transaction, hostParent, hostContainerInfo, context) {
var tag = ReactNativeTagHandles.allocateTag();
this._rootNodeID = tag;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
var updatePayload = ReactNativeAttributePayload.create(this._currentElement.props, this.viewConfig.validAttributes);
var nativeTopRootTag = hostContainerInfo._tag;
UIManager.createView(tag, this.viewConfig.uiViewClassName, nativeTopRootTag, updatePayload);
ReactNativeComponentTree.precacheNode(this, tag);
this.initializeChildren(this._currentElement.props.children, tag, transaction, context);
return tag;
}
这样一来,基本上就完成了React.JS创建一个纯native界面的过程:
网友评论