React Native for Android 原理分析与实践:实现原理
](https://badge.fury.io/js/react-native)
当你拿到 React Native 的源码的时候,它的目录结构是这样的:
jni:ReactNative 的好多机制都是由 C、C++实现的,这部分便是用来载入 SO 库。
perftest:测试配置
proguard:混淆
quicklog:log 输出
react:ReactNative 源码的主要内容,也是我们分析的主要内容。
systrace:system trace
yoga:瑜伽?哈哈,并不是,是 facebook 开源的前端布局引擎
总体来看,整套 React Native 框架分为三层,如下图所示:
Java 层:该层主要提供了 Android 的 UI 渲染器 UIManager(将 JavaScript 映射成 Android Widget)以及一些其他的功能组件(例如:Fresco、Okhttp)等。
C++层:该层主要完成了 Java 与 JavaScript 的通信以及执行 JavaScript 代码两件工作。
JavaScript 层:该层提供了各种供开发者使用的组件以及一些工具库。
注:JSCore,即 JavaScriptCore,JS 解析的核心部分,IOS 使用的是内置的 JavaScriptCore,Androis 上使用的是 https://webkit.org 家的 jsc.so。
通过上面的分析,我们理解了 React Native 的框架结构,除此之外,我们还要理解整套框架里的一些重要角色,如下所示:
ReactContext:ReactContext 继承于 ContextWrapper,是 ReactNative 应用的上下文,通过 getContext()去获得,通过它可以访问 ReactNative 核心类的实现。
ReactInstanceManager:ReactInstanceManager 是 ReactNative 应用总的管理类,创建 ReactContext、CatalystInstance 等类,解析 ReactPackage 生成映射表,并且配合 ReactRootView 管理 View 的创建与生命周期等功能。
CatalystInstance:CatalystInstance 是 ReactNative 应用 Java 层、C++层、JS 层通信总管理类,总管 Java 层、JS 层核心 Module 映射表与回调,三端通信的入口与桥梁。
NativeToJsBridge:NativeToJsBridge 是 Java 调用 JS 的桥梁,用来调用 JS Module,回调 Java。
JsToNativeBridge:JsToNativeBridge 是 JS 调用 Java 的桥梁,用来调用 Java Module。
JavaScriptModule:JavaScriptModule 是 JS Module,负责 JS 到 Java 的映射调用格式声明,由 CatalystInstance 统一管理。
NativeModule:NativeModule 是 ava Module,负责 Java 到 Js 的映射调用格式声明,由 CatalystInstance 统一管理。
JavascriptModuleRegistry:JavascriptModuleRegistry 是 JS Module 映射表,NativeModuleRegistry 是 Java Module 映射表
以上便是整套框架中关键的角色,值得一提的是,当页面真正渲染出来以后,它实际上还是 Native 代码,React Native 的作用就是把 JavaScript 代码映射成 Native 代码以及实现两端 的通信,所以我们在 React Native 基础框架搭建的过程中,指导思路之一就是弱化 Native 与 RN 的边界与区别,让业务开发组感受不到两者的区别,从而简化开发流程。
好,有了对 React Native 框架的整体理解,我们来继续分析一个 RN 页面是如何启动并渲染出来的,这也是我们关心的主要问题。后续的基础框架的搭建、JS Bundle 分包加载、渲染性能优化 等都会围绕着着一块做文章。
二 启动流程
我们知道 RN 的页面也是依托 Activity,React Native 框架里有一个 ReactActivity,它就是我们 RN 页面的容器。ReactActivity 里有个 ReactRootView,正如它的名字那样,它就是 ReactActivity 的 root View,最终渲染出来的 view 都会添加到这个 ReactRootView 上。ReactRootView 调用自己的 startReactApplication()方法启动了整个 RN 页面,在启动的过程 中先去创建页面上下文 ReactContext,然后再去加载、执行并将 JavaScript 映射成 Native Widget,最终一个 RN 页面就显示在了用户面前。
整个 RN 页面的启动流程图如下所示:
这个流程看起来有点长,但实际上重要的东西并不多,我们当前只需要重点关注四个问题:
ReactInstanceManager 是如何被创建的,它在创建的时候都初始化了哪些对象???
RN 页面上下文 ReactContext 在创建的过程中都做了什么,都初始化了哪些对象???
JS Bundle 是如何被加载的???
JS 入口页面是如何被渲染出来的???
2.1 创建 ReactInstanceManager
我们先来看第一个问题,我们都知道要使用 RN 页面,就需要先初始化一个 ReactNativeHost,它是一个抽象类,ReactInstanceManager 就是在这个类里被创建的,如下所示:
public abstract class ReactNativeHost {protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()//应用上下文.setApplication(mApplication)//JSMainModuleP 相当于应用首页的 js Bundle,可以传递 url 从服务器拉取 js Bundle//当然这个只在 dev 模式下可以使用.setJSMainModulePath(getJSMainModuleName())//是否开启 dev 模式.setUseDeveloperSupport(getUseDeveloperSupport())//红盒的回调.setRedBoxHandler(getRedBoxHandler())//JS 执行器.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())//自定义 UI 实现机制,这个我们一般用不到.setUIImplementationProvider(getUIImplementationProvider()).setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
//添加我们外面设置的 Packagefor (ReactPackage reactPackage : getPackages()) {builder.addPackage(reactPackage);}
//获取 js Bundle 的加载路径 String jsBundleFile = getJSBundleFile();if (jsBundleFile != null) {builder.setJSBundleFile(jsBundleFile);} else {builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));}return builder.build();}}
2.2 创建 ReactContext
我们再来看第二个问题,ReactContext 创建流程序列图如下所示:
可以发现,最终创建 ReactContext 是 createReactContext()方法,我们来看看它的实现。
public class ReactInstanceManager {
private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) {Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);//ReactApplicationContext 是 ReactContext 的包装类。final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//debug 模式里开启异常处理器,就是我们开发中见到的调试工具(红色错误框等)if (mUseDeveloperSupport) {reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);}
//创建 JavaModule 注册表 NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null? mNativeModuleCallExceptionHandler: mDevSupportManager;
//创建 CatalystInstanceImpl 的 Builder,它是三端通信的管理类 CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder().setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())//JS 执行器.setJSExecutor(jsExecutor)//Java Module 注册表.setRegistry(nativeModuleRegistry)//JS Bundle 加载器.setJSBundleLoader(jsBundleLoader)//Java Exception 处理器.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cppSystrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");final CatalystInstance catalystInstance;//构建 CatalystInstance 实例 try {catalystInstance = catalystInstanceBuilder.build();} finally {Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);}
if (mBridgeIdleDebugListener != null) {catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);}if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");}ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);//开启加载执行 JS BundlecatalystInstance.runJSBundle();//关联 catalystInstance 与 reactContextreactContext.initializeWithInstance(catalystInstance);
return reactContext;}}
在这个方法里完成了 RN 页面上下文 ReactContext 的创建,我们先来看看这个方法的两个入参:
JSCJavaScriptExecutor jsExecutor:JSCJavaScriptExecutor 继承于 JavaScriptExecutor,当该类被加载时,它会自动去加载"reactnativejnifb.so"库,并会调用 Native 方 法 initHybrid()初始化 C++层 RN 与 JSC 通信的框架。
JSBundleLoader jsBundleLoader:缓存了 JSBundle 的信息,封装了上层加载 JSBundle 的相关接口,CatalystInstance 通过其简介调用 ReactBridge 去加载 JS 文件,不同的场景会创建 不同的加载器,具体可以查看类 JSBundleLoader。
可以看到在 ReactContext 创建的过程中,主要做了以下几件事情:
构建 ReactApplicationContext 对象,ReactApplicationContext 是 ReactContext 的包装类。
利用 jsExecutor、nativeModuleRegistry、jsBundleLoader、exceptionHandler 等参数构建 CatalystInstance 实例,作为以为三端通信的中枢。
调用 CatalystInstance 的 runJSBundle()开始加载执行 JS。
另一个重要的角色 CatalystInstance 出现了,前面我们也说过它是三端通信的中枢。关于通信的具体实现我们会在接下来的通信机制小节来讲述,我们先来接着看 JS 的加载过程。
2.3 加载 JS Bundle
在分析 JS Bundle 的加载流程之前,我们先来看一下上面提到 CatalystInstance,它是一个接口,其实现类是 CatalystInstanceImpl,我们来看看它的构造方法。
public class CatalystInstanceImpl implements CatalystInstance {
private CatalystInstanceImpl(final ReactQueueConfigurationSpec reactQueueConfigurationSpec,final JavaScriptExecutor jsExecutor,final NativeModuleRegistry nativeModuleRegistry,final JSBundleLoader jsBundleLoader,NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");mHybridData = initHybrid();
//创建三大线程:UI 线程、Native 线程与 JS 线程 mReactQueueConfiguration = ReactQueueConfigurationImpl.create(reactQueueConfigurationSpec,new NativeExceptionHandler());mBridgeIdleListeners = new CopyOnWriteArrayList<>();mNativeModuleRegistry = nativeModuleRegistry;//创建 JS Module 注册表实例,这个在以前的代码版本中是在上面的 createReactContext()方法中创建的 mJSModuleRegistry = new JavaScriptModuleRegistry();mJSBundleLoader = jsBundleLoader;mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();mTraceListener = new JSProfilerTraceListener(this);
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");//在 C++层初始化通信桥 initializeBridge(new BridgeCallback(this),jsExecutor,mReactQueueConfiguration.getJSQueueThread(),mNativeModulesQueueThread,mNativeModuleRegistry.getJavaModules(this),mNativeModuleRegistry.getCxxModules());Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());}
}
这个函数的入参大部分我们都已经很熟悉了,我们单独说说这个 ReactQueueConfigurationSpec,它用来创建 ReactQueueConfiguration 的实例,ReactQueueConfiguration 同样是个接口,它的实现类是 ReactQueueConfigurationImpl。
ReactQueueConfiguration 的定义如下:
public interface ReactQueueConfiguration {//UI 线程 MessageQueueThread getUIQueueThread();//Native 线程 MessageQueueThread getNativeModulesQueueThread();//JS 线程 MessageQueueThread getJSQueueThread();void destroy();}
可以看着这个接口的作用就是创建三个带消息队列的线程:
UI 线程:Android 的 UI 线程,处理和 UI 相关的事情。
Native 线程:主要是完成通信的工作。
JS 线程:主要完成 JS 的执行和渲染工作。
可以看到 CatalystInstance 对象在构建的时候,主要做了两件事情:
创建三大线程:UI 线程、Native 线程与 JS 线程。
在 C++层初始化通信桥。
我们接着来看 JS Bundle 的加载流程,JS Bundle 的加载实际上是指 C++层完成的,我们看一下序列图。
注:JS Bundle 有三种加载方式:
setSourceURLs(String deviceURL, String remoteURL) :从远程服务器加载。
loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously):从 Assets 文件夹加载。
loadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously):从文件路径加载。
从这个序列图上我们可以看出,真正加载执行 JS 的地方就是 JSCExector.cpp 的 loadApplicationScript()方法。
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {SystraceSection s("JSCExecutor::loadApplicationScript","sourceURL", sourceURL);...switch (jsStatus) {case JSLoadSourceIsCompiled:if (!bcSourceCode) {throw std::runtime_error("Unexpected error opening compiled bundle");}//调用 JavaScriptCore 里的方法验证 JS 是否有效,并解释执行 evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
flush();
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());return;
case JSLoadSourceErrorVersionMismatch:throw RecoverableError(explainLoadSourceStatus(jsStatus));
case JSLoadSourceErrorOnRead:case JSLoadSourceIsNotCompiled:// Not bytecode, fall through.break;}}...
可以看到这个方法主要是调用 JavaScriptCore 里的 evaluateSourceCode()方法验证 JS 是否有效,并解释执行。然后在调用 flush()方法层调用 JS 层的里 方法执行 JS Bundle。
void JSCExecutor::flush() {SystraceSection s("JSCExecutor::flush");
if (m_flushedQueueJS) {//调用 MessageQueue.js 的 flushedQueue()方法 callNativeModules(m_flushedQueueJS->callAsFunction({}));return;}
// When a native module is called from JS, BatchedBridge.enqueueNativeCall()// is invoked. For that to work, require('BatchedBridge') has to be called,// and when that happens, __fbBatchedBridge is set as a side effect.auto global = Object::getGlobalObject(m_context);auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");// So here, if __fbBatchedBridge doesn't exist, then we know no native calls// have happened, and we were able to determine this without forcing// BatchedBridge to be loaded as a side effect.if (!batchedBridgeValue.isUndefined()) {// If calls were made, we bind to the JS bridge methods, and use them to// get the pending queue of native calls.bindBridge();callNativeModules(m_flushedQueueJS->callAsFunction({}));} else if (m_delegate) {// If we have a delegate, we need to call it; we pass a null list to// callNativeModules, since we know there are no native calls, without// calling into JS again. If no calls were made and there's no delegate,// nothing happens, which is correct.callNativeModules(Value::makeNull(m_context));}}
上面提到 flush()方法调用 MessageQueue.js 的 flushedQueue()方法,这是如何做到的呢???。
事实上这是由 bindBridge()方法来完成的,bindBridge()从__fbBatchedBridge(__fbBatchedBridge 也是被 MessageQueue.js 设置为全局变量,供 C++层读取)对象中取出 MessageQueue.js 里的四个方法:
callFunctionReturnFlushedQueue()
invokeCallbackAndReturnFlushedQueue()
flushedQueue()
callFunctionReturnResultAndFlushedQueue()
并分别存在三个 C++变量中:
m_callFunctionReturnFlushedQueueJS
m_invokeCallbackAndReturnFlushedQueueJS
m_flushedQueueJS
m_callFunctionReturnResultAndFlushedQueueJS
这样 C++就可以调用这四个 JS 方法。
void JSCExecutor::bindBridge() throw(JSException) {SystraceSection s("JSCExecutor::bindBridge");std::call_once(m_bindFlag, [this] {auto global = Object::getGlobalObject(m_context);auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");if (batchedBridgeValue.isUndefined()) {auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");if (!requireBatchedBridge.isUndefined()) {batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});}if (batchedBridgeValue.isUndefined()) {throw JSException("Could not get BatchedBridge, make sure your bundle is packaged correctly");}}
auto batchedBridge = batchedBridgeValue.asObject();m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();});}
至此,JS Bundle 已经加载解析完成,进入 MessageQueue.js 开始执行。
2.4 绑定 ReactContext 与 ReactRootView
JS Bundle 加载完成以后,前面说的 createReactContext()就执行完成了,然后开始执行 setupReacContext()方法,绑定 ReactContext 与 ReactRootView。 我们来看一下它的实现。
public class ReactInstanceManager {
private void setupReactContext(final ReactApplicationContext reactContext) {//...
//执行 Java Module 的初始化 catalystInstance.initialize();//通知 ReactContext 已经被创建爱女 mDevSupportManager.onNewReactContextCreated(reactContext);//内存状态回调设置 mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);//复位生命周期 moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);synchronized (mAttachedRootViews) {//将所有的 ReactRootView 与 catalystInstance 进行绑定 for (ReactRootView rootView : mAttachedRootViews) {attachRootViewToInstance(rootView, catalystInstance);}}//...}
private void attachRootViewToInstance(final ReactRootView rootView,CatalystInstance catalystInstance) {//...UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);//将 ReactRootView 作为根布局 final int rootTag = uiManagerModule.addRootView(rootView);rootView.setRootViewTag(rootTag);//执行 JS 的入口 bundle 页面 rootView.invokeJSEntryPoint();//...}x}
setupReactContext()方法主要完成每个 ReactRootView 与 catalystInstance 的绑定,绑定的过程主要做两件事情:
将 ReactRootView 作为根布局.
执行 JS 的入口 bundle 页面.
JS 的页面入口我们可以设置 mJSEntryPoint 来自定义入口,如果不设置则是默认的入口 AppRegistry。
private void defaultJSEntryPoint() {//...try {//...String jsAppModuleName = getJSModuleName();catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);} finally {Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);}}
这里的调用方式实际上就是原生调用 JS 的方法,它调用的正是我们很熟悉的 AppRegistry.js,AppRegistry.js 调用 runApplication()开始执行 JS 页面的渲染,最终转换为 Native UI 显示在手机上。
到此为止,整个 RN 页面的启动流程就分析完了,我们接着来看看 RN 页面是如何渲染最终显示在手机上的。
三 渲染原理
上面我们也说了,RN 页面的入口一般是 AppRegistry.js,我们就从这个页面入手开始分析 RN 页面的渲染流程。先看一下 RN 页面的渲染流程序列图,如下所示:
这个流程比较长,其实主要是方法的调用链多,原理还是很简单的,我们先概括性的总结一下:
React Native 将代码由 JSX 转化为 JS 组件,启动过程中利用 instantiateReactComponent 将 ReactElement 转化为复合组件 ReactCompositeComponent 与元组件 ReactNativeBaseComponent,利用 ReactReconciler 对他们进行渲染。
UIManager.js 利用 C++层的 Instance.cpp 将 UI 信息传递给 UIManagerModule.java,并利用 UIManagerModule.java 构建 UI。
UIManagerModule.java 接收到 UI 信息后,将 UI 的操作封装成对应的 Action,放在队列中等待执行。各种 UI 的操作,例如创建、销毁、更新等便在队列里完成,UI 最终 得以渲染在屏幕上。
3.1 JavaScript 层组件渲染
如上图所示 AppRegistry.registerComponent 用来注册组件,在该方法内它会调用 AppRegistry.runApplication()来启动 js 的渲染流程。AppRegistry.runApplication() 会将传入的 Component 转换成 ReactElement,并在外面包裹一层 AppContaniner,AppContaniner 主要用来提供一些 debug 工具(例如:红盒)。
如下所示:
function renderApplication<Props: Object>( RootComponent: ReactClass<Props>,initialProps: Props,rootTag: any ) {invariant(rootTag,'Expect to have a valid rootTag, instead got ', rootTag);ReactNative.render(<AppContainer rootTag={rootTag}><RootComponent{...initialProps}rootTag={rootTag}/></AppContainer>,rootTag);}
我们抛开函数调用链,分析其中关键的部分,其他部分都是简单的函数调用。
ReactNativeMount.renderComponent()
instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)
ReactNativeMount.renderComponent()
/**
@param {ReactComponent} instance Instance to render.
@param {containerTag} containerView Handle to native view tag/renderComponent: function( nextElement: ReactElement<>,containerTag: number,callback?: ?(() => void)): ?ReactComponent<any, any, any> {
//将 RectElement 使用相同的 TopLevelWrapper 进行包裹 var nextWrappedElement = React.createElement(TopLevelWrapper,{ child: nextElement });
var topRootNodeID = containerTag;var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];if (prevComponent) {var prevWrappedElement = prevComponent._currentElement;var prevElement = prevWrappedElement.props.child;if (shouldUpdateReact
Component(prevElement, nextElement)) {ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement, emptyObject);if (callback) {ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);}return prevComponent;} else {ReactNativeMount.unmountComponentAtNode(containerTag);}}
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {console.error('You cannot render into anything but a top root');return null;}
ReactNativeTagHandles.assertRootTag(containerTag);
//检查之前的节点是否已经 mount 到目标节点上,如果有则进行比较处理 var instance = instantiateReactComponent(nextWrappedElement, false);ReactNativeMount._instancesByContainerID[containerTag] = instance;
// The initial render is synchronous but any updates that happen during// rendering, in componentWillMount or componentDidMount, will be batched// according to the current batching strategy.
//将 mount 任务提交给回调 Queue,最终会调用 ReactReconciler.mountComponent()ReactUpdates.batchedUpdates(batchedMountComponentIntoNode,instance,containerTag);var component = instance.getPublicInstance();if (callback) {callback.call(component);}return component;},
该方法主要做了以下事情:
将传入的 RectElement 使用相同的 TopLevelWrapper 进行包裹,生成 nextWrappedElement。
检查之前的节点是否已经 mount 到目标节点上,如果有则进行比较处理,将上一步生成的 nextWrappedElement 传入 instantiateReactComponent(nextWrappedElement, false)方法。
将 mount 任务提交给回调 Queue,最终会调用 ReactReconciler.mountComponent(),ReactReconciler.mountComponent()又会去调用 C++层 Instance::mountComponent() 方法。
instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)
在分析这个函数之前,我们先来补充一下 React 组件相关知识。React 组件可以分为两种:
元组件:框架内置的,可以直接使用的组件。例如:View、Image 等。它在 React Native 中用 ReactNativeBaseComponent 来描述。
复合组件:用户封装的组件,一般可以通过 React.createClass()来构建,提供 render()方法来返回渲染目标。它在 React Native 中用 ReactCompositeComponent 来描述。
instantiateReactComponent(node, shouldHaveDebugID)方法根据对象的 type 生成元组件或者复合组件。
/**
Given a ReactNode, create an instance that will actually be mounted.
@param {ReactNode} node
@param {boolean} shouldHaveDebugID
@return {object} A new instance of the element's constructor.
@protected*/
评论