reactnativeReact Native源码分析

ReactNative源码分析 - 渲染原理

2019-12-15  本文已影响0人  浮游lb

1.ReactNative源码分析 - 概述
2.ReactNative源码分析 - JavaScriptCore C语言篇
3.ReactNative源码分析 - 启动流程
4.ReactNative源码分析 - 通信机制
5.ReactNative源码分析 - 渲染原理

一、前言

二、React简介

import React from 'react';
import {View, TouchableOpacity} from 'react-native';

export default class TestRender extends React.Component {
    render() {
     return  (
        <View style={{ backgroundColor: 'white', paddingTop: 64,}} testID="white">
            <View style={{ backgroundColor: "yellow",width: 50, height: 50}} testID="white-yellow"/>
            <View 
                style={{backgroundColor: 'green',width: 100, height: 100,justifyContent:"center",alignItems:"center"}} testID="white-green"
            >
                <View style={{backgroundColor: 'red', width: 50, height: 50,}} testID="white-green-red"></View>
            </View>
            <TouchableOpacity
                style={{backgroundColor: 'blue', width: 50, height: 50}}
                testID="white-blue"
                onPress={() => console.log("onPress") }
            />
        </View>
    );
    }
}

上述JSX转码后变成如下代码。每个React元素转化为React.createElement函数调用,最终返回一个描述组件信息的JS对象,其中嵌套关系表示父子组件关系。

export default class TestRender extends React.Component {
    render() {
      return 
        React.createElement(View, {
            style: {
                backgroundColor: 'white',
                paddingTop: 64
            },
            testID: "white"
        }, React.createElement(View, {
                style: {
                    backgroundColor: "yellow",
                    width: 50,
                    height: 50
                },
                testID: "white-yellow"
            }),
            React.createElement(View, {
                style: {
                    backgroundColor: 'green',
                    width: 100,
                    height: 100,
                    justifyContent: "center",
                    alignItems: "center"
                },
                testID: "white-green"
            }, React.createElement(View, {
                    style: {
                        backgroundColor: 'red',
                        width: 50,
                        height: 50
                    },
                    testID: "white-green-red"
                })), 
            React.createElement(TouchableOpacity, {
                style: {
                    backgroundColor: 'blue',
                    width: 50,
                    height: 50
                },
                testID: "white-blue",
                onPress: () => console.log("onPress")})
        );
    }
  }
const element = <h1>Hello, world</h1>;

三、原生端与渲染流程相关的类

1.原生端UI组件体系

View体系.jpg

原生端与UI渲染相关的视图类体系如图所示,ReactNative封装了一系列原生控件,把控件信息导出到JS端并且包装为对应的组件供JS业务层使用,我们暂且把前者称为控件,后者称为组件,两者在Native端、JS端一一对应。

2.原生端UI管理者体系

Manager体系.jpg

原生端与UI渲染相关的管理者类体系如图所示

四、原生控件信息如何传递到JS端

这里以RCTWKWebView为例分析原生控件信息导出流程,理解RCTWKWebView的封装,你一定可以封装自定义原生控件供JS端使用

1.RCTViewManager定义导出属性

RCTViewManager控件属性导出宏生成的对应函数,用于后续获取导出属性信息

// RCTWKWebViewManager.m
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RCTWKWebView) {
  view.bounces = json == nil ? true : [RCTConvert BOOL: json];
}
// 生成属性type keypath获取函数
+ (NSArray<NSString *> *)propConfig_source { 
    return @[@"NSDictionary"]; 
}
+ (NSArray<NSString *> *)propConfig_onLoadingStart { 
    return @[@"RCTDirectEventBlock"]; 
}

+ (NSArray<NSString *> *)propConfig_bounces { 
    return @[@"BOOL", @"__custom__"]; 
} 
- (void)set_bounces:(id)json forView:(RCTWKWebView *)view withDefaultView:(RCTWKWebView *)defaultView {
    view.bounces = json == ((void *)0) ? 1 : [RCTConvert BOOL: json];
}
// RCTViewManager.m
RCT_EXPORT_SHADOW_PROPERTY(marginTop, YGValue)
+ (NSArray<NSString *> *)propConfigShadow_marginTop { 
    return @[@"YGValue"]; 
}

2.RCTUIManager收集原生控件信息

// RCTUIManager.m
- (void)setBridge:(RCTBridge *)bridge
{
  _bridge = bridge;
  ...
  // 收集RCTViewManager
  _componentDataByName = [NSMutableDictionary new];
  for (Class moduleClass in _bridge.moduleClasses) {
    if ([moduleClass isSubclassOfClass:[RCTViewManager class]]) {
      RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:moduleClass bridge:_bridge];
      _componentDataByName[componentData.name] = componentData;
    }
  }
}
// RCTComponentData.m

// 获取控件导出属性
- (NSDictionary<NSString *, id> *)viewConfig
{
  NSMutableArray<NSString *> *bubblingEvents = [NSMutableArray new];
  NSMutableArray<NSString *> *directEvents = [NSMutableArray new];
  
  // Runtime获取属性/布局属性导出宏生成的函数,函数前缀`propConfig`
  unsigned int count = 0;
  NSMutableDictionary *propTypes = [NSMutableDictionary new];
  Method *methods = class_copyMethodList(object_getClass(_managerClass), &count);
  for (unsigned int i = 0; i < count; i++) {
    SEL selector = method_getName(methods[i]);
    const char *selectorName = sel_getName(selector);
    
    // 过滤无propConfig前缀的函数
    if (strncmp(selectorName, "propConfig", strlen("propConfig")) != 0) {
      continue;
    }

    // 控件导出的属性/布局属性生成的函数分别是propConfig_* propConfigShadow_* ,定位'_'的位置,进而获取属性名
    const char *underscorePos = strchr(selectorName + strlen("propConfig"), '_');
    if (!underscorePos) {
      continue;
    }
    NSString *name = @(underscorePos + 1);
    
    // 调用函数获取属性类型
    NSString *type = ((NSArray<NSString *> *(*)(id, SEL))objc_msgSend)(_managerClass, selector)[0];
    
    // 回调类型属性,用BOOL代替
    if ([type isEqualToString:@"RCTBubblingEventBlock"]) {
      [bubblingEvents addObject:RCTNormalizeInputEventName(name)];
      propTypes[name] = @"BOOL";
    } else if ([type isEqualToString:@"RCTDirectEventBlock"]) {
      [directEvents addObject:RCTNormalizeInputEventName(name)];
      propTypes[name] = @"BOOL";
    } else {
      propTypes[name] = type;
    }
  }
  free(methods);
  
  Class superClass = [_managerClass superclass];
  
  return @{
    @"propTypes": propTypes,
    @"directEvents": directEvents,
    @"bubblingEvents": bubblingEvents,
    @"baseModuleName": superClass == [NSObject class] ? (id)kCFNull : moduleNameForClass(superClass),
  };
}

例子RCTWKWebViewManager返回结果如下
propTypes存放所有导出属性的属性名、类型;
directEvents、bubblingEvents存放回调类型;
baseModuleName表示控件基类,用于在JS端添加基类导出属性以得到完整属性表。

{
     "propTypes": {
        "allowsInlineMediaPlayback": "BOOL",
        "automaticallyAdjustContentInsets":"BOOL",
        "bounces" : "BOOL",
        "contentInset": "UIEdgeInsets",
        ...
        "onMessage":""BOOL,
        "onShouldStartLoadWithRequest":"BOOL"
     },
     "directEvents": [
        "topLoadingStart",
        "topLoadingFinish",
        "topLoadingError",
        "topMessage",
        "topShouldStartLoadWithRequest"
     ],
     "bubblingEvents": [],
     "baseModuleName": "RCTViewManager"
   }
// 收集所有导出供JS端使用的原生控件的信息
- (NSDictionary<NSString *, id> *)constantsToExport
{
  return [self getConstants];
}

- (NSDictionary<NSString *, id> *)getConstants
{
  NSMutableDictionary<NSString *, NSDictionary *> *constants = [NSMutableDictionary new];
  NSMutableDictionary<NSString *, NSDictionary *> *directEvents = [NSMutableDictionary new];
  NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents = [NSMutableDictionary new];
  
  [_componentDataByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
    NSMutableDictionary<NSString *, id> *moduleConstants = moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
    constants[name] = moduleConstants;
  }];
  return constants;
}

static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
    NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
    NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
    RCTComponentData *componentData) {
  NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
 
  NSMutableDictionary<NSString *, NSDictionary *> *bubblingEventTypes = [NSMutableDictionary new];
  NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];

  moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);

  NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
  moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
  moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
  moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
  moduleConstants[@"directEventTypes"] = directEventTypes;

  for (NSString *eventName in viewConfig[@"directEvents"]) {
    if (!directEvents[eventName]) {
      directEvents[eventName] = @{
                                  @"registrationName": [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"],
                                  };
    }
    directEventTypes[eventName] = directEvents[eventName];
  }

  // Add bubbling events
  for (NSString *eventName in viewConfig[@"bubblingEvents"]) {
    if (!bubblingEvents[eventName]) {
      NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"];
      bubblingEvents[eventName] = @{
                                    @"phasedRegistrationNames": @{
                                        @"bubbled": bubbleName,
                                        @"captured": [bubbleName stringByAppendingString:@"Capture"],
                                        }
                                    };
    }
    bubblingEventTypes[eventName] = bubblingEvents[eventName];
  }

  return moduleConstants;
}

最终生成一张表包含所有原生控件导出属性信息<原生控件名:导出信息>,格式如下。作为RCTUIManager原生模块常量导出到JS端,原生模块信息导出流程详解通信机制

{
   "RCTWKWebView": {
     "Manager": "WKWebViewManager",
     "NativeProps" : {
         "allowsInlineMediaPlayback" :"BOOL",
         ...
         "onLoadingError" : "BOOL",
         "onLoadingFinish" : "",
         "onLoadingStart" : "BOOL",
         "onMessage" : "BOOL",
         "source" : "NSDictionary",
      },
      "baseModuleName" : "RCTView",
      "bubblingEventTypes" : { };
      "directEventTypes" {
         "topLoadingError" : { "registrationName" : "onLoadingError",},
         "topLoadingFinish" : { "registrationName" : "onLoadingFinish",},
         ...
       },
    },
   
   "RCTView": {...},
   
   ...
   }

3.UIManager获取原生控件信息

原生控件管理者ViewManager通常不会包装,原因是原生模块信息几乎都通过UIManager获取,不会直接使用。但原生控件信息导出到JS端,会包装为对应组件,例如RCTWKWebView在JS端会包装为WebView.ios.js,这么做的好处同上。

// UIManager.js

const {UIManager} = NativeModules;
// 存放所有原生控件信息 { viewName: viewConfig }
const viewManagerConfigs = {};

// 获取控件信息
UIManager.getViewManagerConfig = function(viewManagerName: string) {
  const config = viewManagerConfigs[viewManagerName];
  if (config) {
    return config;
  }

  // 防御作用,获取不到viewConfig时,使用同步函数获取试图获取
  if (UIManager.lazilyLoadView && !triedLoadingConfig.has(viewManagerName)) {
    ...
  }
  return viewManagerConfigs[viewManagerName];
};

// 原生控件配置信息viewConfig
function lazifyViewManagerConfig(viewName) {
  const viewConfig = UIManager[viewName];
  if (viewConfig.Manager) {
    // viewConfig存入组件信息表viewManagerConfigs
    viewManagerConfigs[viewName] = viewConfig;

    // 定义取值函数getter,用于获取Constants,即ViewManager导出常量
    defineLazyObjectProperty(viewConfig, 'Constants', {
      get: () => {
        const viewManager = NativeModules[viewConfig.Manager];
        const constants = {};
        viewManager &&
          Object.keys(viewManager).forEach(key => {
            const value = viewManager[key];
            if (typeof value !== 'function') {
              constants[key] = value;
            }
          });
        return constants;
      },
    });
    
    // 定义取值函数,用于获取Commands,即ViewManager导出函数(索引)
    defineLazyObjectProperty(viewConfig, 'Commands', {
      get: () => {
        const viewManager = NativeModules[viewConfig.Manager];
        const commands = {};
        let index = 0;
        viewManager &&
          Object.keys(viewManager).forEach(key => {
            const value = viewManager[key];
            if (typeof value === 'function') {
              commands[key] = index++;
            }
          });
        return commands;
      },
    });
  }
}

// 遍历UIManager模块信息,获取、加工组件信息,并缓存
if (Platform.OS === 'ios') {
  Object.keys(UIManager).forEach(viewName => {
    lazifyViewManagerConfig(viewName);
  });
}

// 导出UIManager对象,单例
module.exports = UIManager;

最终原生控件信息存放在viewManagerConfigs,格式如下

{
    RCTWKWebView: {
        Commands: {
             getConstants: 7,
             goBack: 1,
             goForward: 2,
             ...
        },
        Constants: {},
        Manager: "WKWebViewManager",
        NativeProps: {
            allowsInlineMediaPlayback: "BOOL",
            bounces: "BOOL",
            ...
            onMessage: "BOOL",
        },
        baseModuleName: "RCTView",
        bubblingEventTypes:{ },
        directEventTypes: {
            topLoadingStart: {registrationName: "onLoadingStart"},
            topMessage: {registrationName: "onMessage"},
            ...
         }
    },
    
    RCTView: {
        ...
    }
    
    ...
}

4.原生控件信息处理、获取

上诉流程中,UIManager单例收集所有原生控件信息,但还不能直接使用,下面进一步分析信息的处理、使用流程。

原生控件信息传递.jpg
// WebView.ios.js
const RCTWKWebView = requireNativeComponent('RCTWKWebView');

该函数其实是createReactNativeComponentClass函数的一层包装,传入一个回调函数。

// requireNativeComponent.js
const requireNativeComponent = (uiViewClassName: string): string =>
    createReactNativeComponentClass(uiViewClassName, () =>
        getNativeComponentAttributes(uiViewClassName),
);

getNativeComponentAttributes,负责从UIManager获取原生控件信息,并做进一步加工:添加基类属性;添加属性比较/处理函数。最终得到完整的原生控件信息。它作为回调函数存放到ReactNativeViewConfigRegistry中,以懒加载完整的原生控件信息。

// 获取原生控件信息
// getNativeComponentAttributes.js 
function getNativeComponentAttributes(uiViewClassName: string) {
  // 从UIManager获取原生控件信息
  const viewConfig = UIManager.getViewManagerConfig(uiViewClassName);

  // 添加基类原生模块信息
  let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
  let nativeProps = viewConfig.NativeProps;
  while (baseModuleName) {
    const baseModule = UIManager.getViewManagerConfig(baseModuleName);
    ...
  }

  // 添加属性比较、处理函数
  const validAttributes = {};
  for (const key in nativeProps) {
    const typeName = nativeProps[key];
    const diff = getDifferForType(typeName);
    const process = getProcessorForType(typeName);
    validAttributes[key] =
      diff == null && process == null ? true : {diff, process};
  }

  Object.assign(viewConfig, {
    uiViewClassName,  
    validAttributes,  
    bubblingEventTypes,
    directEventTypes,
  });
  return viewConfig;
}
// 注册原生控件信息获取回调
// createReactNativeComponentClass.js
const createReactNativeComponentClass = function(
  name: string,
  callback: ViewConfigGetter,
): string {
  return register(name, callback);
};

ReactNativeViewConfigRegistry模块就是一个原生控件信息注册机,它包含所有需要的原生控件信息,并且是懒加载机制。
调用register函数注册原生控件信息获取回调,并原路返回传入的view name,作为组件名书写JSX。

// ReactNativeViewConfigRegistry.js

const viewConfigCallbacks = new Map(); // 原生控件信息获取回调函数表
const viewConfigs = new Map();         // 原生控件信息表

// 注册原生控件信息获取回调,用于从UIManager获取组件信息
exports.register = function(name: string, callback: ViewConfigGetter): string {
  viewConfigCallbacks.set(name, callback);
  return name;
};

 // 获取原生控件信息,懒加载机制
 exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
  let viewConfig;
  if (!viewConfigs.has(name)) {
    const callback = viewConfigCallbacks.get(name);
    viewConfigCallbacks.set(name, null);
    viewConfig = callback();
    processEventTypes(viewConfig);
    viewConfigs.set(name, viewConfig);
  } else {
    viewConfig = viewConfigs.get(name);
  }
  return viewConfig;
};

总结
1.封装组件时调用requireNativeComponent注册一个原生控件信息获取/处理回调到原生控件信息注册机ReactNativeViewConfigRegistry
2.真正执行渲染,使用到对应组件,调用get函数从原生控件信息注册机获取原生控件信息,若控件信息存在,则直接使用;否则执行原生控件信息获取/处理回调,以得到完整并原生控件信息并缓存。
这一流程驱使原生控件数据进行加工,从UIManager流行ReactNativeViewConfigRegistry,并且具备懒加载特性。

五、渲染流程

1.注册JS组件

// index.js
AppRegistry.registerComponent('Main', () => Main);
// AppRegistry.js

const runnables: Runnables = {};

const AppRegistry = {
  // 注册组件
  registerComponent(appKey: string, componentProvider: ComponentProvider, section?: boolean): string {
    runnables[appKey] = {
      componentProvider,
      run: ...
    };
    return appKey;
  },

  // 运行组件:获取注册表中的组件,运行
  runApplication(appKey: string, appParameters: any): void {
    runnables[appKey].run(appParameters);
  },

  // 卸载组件
  unmountApplicationComponentAtRootTag(rootTag: number): void {
    ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
  },
}

// 注册JS模块 AppRegistry
BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);

2.运行JS组件,执行渲染

// RCTRootView.m
- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  NSDictionary *appParameters = @{
    @"rootTag": _contentView.reactTag,
    @"initialProps": _appProperties ?: @{},
  };
  
  // 调用js模块AppRegistry runApplication,运行组件
  [bridge enqueueJSCall:@"AppRegistry"
                 method:@"runApplication"
                   args:@[moduleName, appParameters]
             completion:NULL];
}
// AppRegistry.js

runnables[appKey] = {
 // 组件获取函数
 componentProvider,
 // 组件运行函数, 执行render
 run: appParameters => {
   renderApplication(
     componentProviderInstrumentationHook(componentProvider), 
     appParameters.initialProps,  // 原生层传递过来的初始化属性
     appParameters.rootTag,       // rootTag
     wrapperComponentProvider && wrapperComponentProvider(appParameters),
     appParameters.fabric,
   );
 },
};
// renderApplication.js

function renderApplication<Props: Object>(
  RootComponent: React.ComponentType<Props>,  // 根组件
  initialProps: Props,
  rootTag: any,  
  WrapperComponent?: ?React.ComponentType<*>,
  fabric?: boolean,
  showFabricIndicator?: boolean,) 
{
  // 根组件RootComponent嵌入容器组件AppContainer(用于包装yellowBox等调试组件)
  let renderable = (
    <AppContainer rootTag={rootTag} WrapperComponent={WrapperComponent}>
      <RootComponent {...initialProps} rootTag={rootTag} />
    </AppContainer>
  );

  if (fabric) {
    require('ReactFabric').render(renderable, rootTag);
  } else {
    // 执行渲染
    require('ReactNative').render(renderable, rootTag);
  }
}
// ReactNative.js

if (__DEV__) {
  ReactNative = require('ReactNativeRenderer-dev');
} else {
  ReactNative = require('ReactNativeRenderer-prod');
}
15754700576459.jpg

3.获取React计算结果

React与ReactNative的衔接是个大工程,此处省略一万字,直接分析两者最终的计算结果如何驱动原生端执行UI渲染。

Debug环境下,调用ReactNativeRenderer-dev.js导出对象ReactNativeRenderer的渲染函数render执行渲染。经过一个长长的调用栈之后会执行根组件的render(可在根组件render打断点追踪调用栈),返回一个根React Element。这就是React计算结果了,即我们用React编写的JSX代码的最终产物。
DEMO中的例子TestRender,最终返回React Element对象如下(省略了部分信息,testID为测试id),这其实就是一棵树,包含原生控件信息。

{
    $$typeof: Symbol(react.element),
    type: {$$typeof: Symbol(react.forward_ref), displayName: "View",},
    props: {
        children: [
            {
                $$typeof: Symbol(react.element),
                type: {$$typeof: Symbol(react.forward_ref), displayName: "View"},
                props: {
                    style: {backgroundColor: "yellow", width: 50, height: 50},
                    testID: "white-yellow"
                },
            },
            {
                $$typeof: Symbol(react.element),
                type: {$$typeof: Symbol(react.forward_ref), displayName: "View"},
                props:{
                    children: [{
                        $$typeof: Symbol(react.element),
                        props:{
                            style: {backgroundColor: "red", width: 50, height: 50},
                            testID: "white-green-red"
                        },
                        type: {$$typeof: Symbol(react.forward_ref), displayName: "View"}
                    }],
                    style: {backgroundColor: "green", width: 100, height: 100},
                    testID: "white-green",
                },
            },
            {
                $$typeof: Symbol(react.element),
                type: {displayName: "TouchableOpacity"},
                props: {
                    style: {backgroundColor: "blue", width: 50, height: 50}, 
                    testID: "white-blue", 
                    activeOpacity: 0.2, 
                    onPress: ƒ
                },
            }
        ],
        style: {backgroundColor: "white", paddingTop: 64},
        testID: "white",
    },
}

4.驱动Native执行渲染流程

@implementation RCTUIManager
{
  // 根控件集合 reactTag <reactTag>
  NSMutableSet<NSNumber *> *_rootViewTags;
  // 暂存UI操作;
  NSMutableArray<RCTViewManagerUIBlock> *_pendingUIBlocks;
  // 布局结点集合 { reactTag : RCTShadowView }
  NSMutableDictionary<NSNumber *, RCTShadowView *> *_shadowViewRegistry; // RCT thread only
  
  // 控件集合 {reactTag : UIView}
  NSMutableDictionary<NSNumber *, UIView *> *_viewRegistry; // Main thread only
  
  // 已更新属性的shadowView集合 { RCTShadowView: [props key] }
  NSMapTable<RCTShadowView *, NSArray<NSString *> *> *_shadowViewsWithUpdatedProps; // UIManager queue only.
  // 已更新子控件的shadowView集合
  NSHashTable<RCTShadowView *> *_shadowViewsWithUpdatedChildren; // UIManager queue only.
  ...
}

1.JS端根据计算结果,驱动原生端创建原生控件

// ReactNativeRenderer-dev.js

function createInstance(
  type,
  props,
  rootContainerInstance,
  hostContext,
  internalInstanceHandle
) {
  // 创建组件tag
  var tag = allocateTag();
  // 获取原生控件信息
  var viewConfig = ReactNativeViewConfigRegistry.get(type);
  var updatePayload = create(props, viewConfig.validAttributes);
  
  // 调用原生模块,创建原生控件
  UIManager.createView(
    tag, // reactTag
    viewConfig.uiViewClassName, // viewName
    rootContainerInstance, // rootTag
    updatePayload // props
  );

  var component = new ReactNativeFiberHostComponent(tag, viewConfig);
  precacheFiberNode(internalInstanceHandle, tag);
  updateFiberProps(tag, props);
  return component;
}
// RCTUIManager.m

// 创建原生控件
RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
                  viewName:(NSString *)viewName
                  rootTag:(nonnull NSNumber *)rootTag
                  props:(NSDictionary *)props)
{  
  RCTComponentData *componentData = _componentDataByName[viewName];
 
  // 创建对应的shadowView,存入容器_shadowViewRegistry,设置属性
  RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
  if (shadowView) {
    [componentData setProps:props forShadowView:shadowView];
    _shadowViewRegistry[reactTag] = shadowView;
    RCTShadowView *rootView = _shadowViewRegistry[rootTag];
    shadowView.rootView = (RCTRootShadowView *)rootView;
  }

  // 主线程 创建NativeView,存入View注册表_viewRegistry
  __block UIView *preliminaryCreatedView = nil;
  void (^createViewBlock)(void) = ^{
    if (preliminaryCreatedView) {
      return;
    }
    preliminaryCreatedView = [componentData createViewWithTag:reactTag];
    if (preliminaryCreatedView) {
      self->_viewRegistry[reactTag] = preliminaryCreatedView;
    }
  };
  RCTExecuteOnMainQueue(createViewBlock);
  
  // 设置控件属性(对UIView的操作都先暂存在_pendingUIBlocks,批处理)
  [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
    createViewBlock();
    if (preliminaryCreatedView) {
      [componentData setProps:props forView:preliminaryCreatedView];
    }
  }];

  // 更新props,存入_shadowViewsWithUpdatedProps
  [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
}

2.根据计算结果,设置视图层次结构

// ReactNativeRenderer-dev.js

function finalizeInitialChildren( parentInstance, type, props, rootContainerInstance, hostContext) {
  if (parentInstance._children.length === 0) {
    return false;
  }
  var nativeTags = parentInstance._children.map(function(child) {
    return typeof child === "number" ? child // Leaf node (eg text): child._nativeTag;
  });

  UIManager.setChildren(
    parentInstance._nativeTag, // 父控件tag
    nativeTags // 子控件tag集合
  );
  return false;
}

原生端布局结点树更改层次结构,此时仅从数据层面改变层次结构,并未真正addSubVew(详见UIView+React.m)

// RCTUIManager.m

RCT_EXPORT_METHOD(setChildren:(nonnull NSNumber *)containerTag
                  reactTags:(NSArray<NSNumber *> *)reactTags)
{  
  RCTSetChildren(containerTag, reactTags,(NSDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry);
  
  [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
    // 仅仅是数据层面,并不正真改成视图层次
    RCTSetChildren(containerTag, reactTags,(NSDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry);
  }];

  [self _shadowViewDidReceiveUpdatedChildren:_shadowViewRegistry[containerTag]];
}

3.布局时机一到,真正执行UI渲染

// RCTUIManager.m

// 布局时机
- (void)batchDidComplete
{
  [self _layoutAndMount];
}

- (void)_layoutAndMount
{
  // 通知 RCTShadowView、RCTComponent 属性已更新
  [self _dispatchPropsDidChangeEvents];
  // 通知 RCTShadowView、RCTComponent 控件/布局结点层次结构已更新(subView已改变)
  [self _dispatchChildrenDidChangeEvents];

  // 构建一个UI渲染、执行布局动画的Block,添加到_pendingUIBlocks
  for (NSNumber *reactTag in _rootViewTags) {
    RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
    [self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
  }

  // 统一执行所有 _pendingUIBlocks,包括UI渲染
  [self flushUIBlocksWithCompletion:^{
    [self->_observerCoordinator uiManagerDidPerformMounting:self];
  }];
}

// 构建一个UI渲染、执行布局动画的Block
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
{
    ...
    return block;
}

至此,UI渲染的基本流程就分析完毕了。

六、JS端调用原生控件导出函数

JS端调用原生控件的导出函数,流程如下

// RCTUIManager.m

/*
 向ViewManager派发命令:用于RN端调用控件导出函数
 reactTag 控件标签
 commandID 命令id
 commandArgs 参数
 */
RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
                  commandID:(NSInteger)commandID
                  commandArgs:(NSArray<id> *)commandArgs)
{
  RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
  RCTComponentData *componentData = _componentDataByName[shadowView.viewName];
  Class managerClass = componentData.managerClass;
  RCTModuleData *moduleData = [_bridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
  id<RCTBridgeMethod> method = moduleData.methods[commandID];

  // 带上reactTag,用于获取对应Native控件
  NSArray *args = [@[reactTag] arrayByAddingObjectsFromArray:commandArgs];
  // 调用控件导出函数
  [method invokeWithBridge:_bridge module:componentData.manager arguments:args];
}
// RCTWKWebViewManager.m

RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
{
  [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWKWebView *> *viewRegistry) {
    RCTWKWebView *view = viewRegistry[reactTag];
    if (![view isKindOfClass:[RCTWKWebView class]]) {  
    } else {
      [view goBack];
    }
  }];
}

七、结语

Reference

ReactNative中文网
React
ReactNative源码解析——渲染机制详解

上一篇下一篇

猜你喜欢

热点阅读