Loading... # React Native渲染流程 # 1. 图片描述  # 2. 文字描述 ## 2.1 runApplication 在RN侧通过`registerComponent`注册`runnables`,而后在安卓侧通过 ```java catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); ``` 调用`runApplication`,传入对应的appName和一些参数,就会对应RN侧`AppRegistry.js`中的`runApplication`,最终会走到`renderApplication`里执行渲染逻辑 ## 2.2 renderApplication 进入其中会发现有两个选择,ReactFabric和ReactNative,Fabric就是RN的新架构,现在最新的代码还是用的ReactNative ## 2.3 ReactNative.render ```jsx function render(element, containerTag, callback) { var root = roots.get(containerTag); if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. root = createContainer(containerTag, LegacyRoot, false, null, false); roots.set(containerTag, root); } // 进入这里继续执行渲染 updateContainer(element, root, null, callback); // $FlowIssue Flow has hardcoded values for React DOM that don't work with RN return getPublicRootInstance(root); } ``` 在updateContainer里会创建一个update,并且插入到队列里,然后执行队列,接下来就是对组件树的遍历了 ```jsx var update = createUpdate(eventTime, lane); update.payload = { element: element }; enqueueUpdate(current$1, update); var root = scheduleUpdateOnFiber(current$1, lane, eventTime); ``` 接下来就是不停地检查、插入队列、根据优先级处理(但这里是串行的,并没有异步),这里省略具体代码,依次的函数调用顺序如下: > updateContainer → enqueueUpdate → scheduleUpdateOnFiber → performSyncWorkOnRoot → renderRootSync → workLoopSync → performUnitOfWork 在workLoopSync里,我们可以看到一个while循环,这里就开始遍历组件树了 ```jsx function workLoopSync() { // Already timed out, so perform work without checking if we need to yield. while (workInProgress !== null) { performUnitOfWork(workInProgress); } } ``` react遍历树的时候有两个重要的函数`performUnitOfWork`和`completeUnitOfWork`。`performUnitOfWork`就是深度遍历到底,然后执行`completeUnitOfWork`回退,同时创建对应的dom/Native组件。也就是先创建的子节点再创建父节点,看下performUnitOfWork的部分代码: ```jsx function performUnitOfWork(unitOfWork) { // 开始处理,会返回子组件fiber实例,用于深度循环遍历,把任务加入队列 var next; if (next === null) { // 不存在子级fiber,完成当前单元任务的处理。 next = completeUnitOfWork(unitOfWork); } } ``` 这样就完成了一个子集任务的内容 在completeUnitOfWork里,主要就是找父组件回退、找兄弟组件继续遍历: ```jsx function completeUnitOfWork(unitOfWork: Fiber): Fiber | null { workInProgress = unitOfWork; do { //完成当前的工作 next = completeWork(current, workInProgress, renderExpirationTime); //兄弟组件 const siblingFiber = workInProgress.sibling; if (siblingFiber !== null) { //返回兄弟组件继续遍历 return siblingFiber; } //否则回到父组件继续完成工作 workInProgress = returnFiber; } while (workInProgress !== null); return null; } ``` 从这里可以看出,React遍历组件树深度遍历走到底了,就算作一个单元,完成当前的渲染工作 这样做的好处是,可以把遍历工作分散成小单元工作。这也是Fiber的一个重要设计思路。可以避免一次渲染大量组件而阻塞了线程。导致用户操作没有响应 ## 2.4 createInstance 创建组件是在completeWork里完成的。里面有很多不同类型的组件。里面涉及创建真实渲染的Dom或Native组件的是`HostComponent`,这个组件最后会调用`createInstance`来创建组件 ```jsx function createInstance( type, props, rootContainerInstance, hostContext, internalInstanceHandle ) { var tag = allocateTag(); var viewConfig = getViewConfigForType(type); { for (var key in viewConfig.validAttributes) { if (props.hasOwnProperty(key)) { ReactNativePrivateInterface.deepFreezeAndThrowOnMutationInDev( props[key] ); } } } var updatePayload = create(props, viewConfig.validAttributes); ReactNativePrivateInterface.UIManager.createView( tag, // reactTag viewConfig.uiViewClassName, // viewName rootContainerInstance, // rootTag updatePayload // props ); var component = new ReactNativeFiberHostComponent( tag, viewConfig, internalInstanceHandle ); precacheFiberNode(internalInstanceHandle, tag); updateFiberProps(tag, props); // Not sure how to avoid this cast. Flow is okay if the component is defined // in the same file but if it's external it can't see the types. return component; } ``` 这里主要是调用了UIManager的createView方法,传入了tag、viewName、rootTag、props参数信息。这里的UIManager实际上是映射到Java里的一个class--- `UIManagerModule`,对应的函数如下: ```java @ReactMethod publicvoidcreateView(inttag, String className,introotViewTag,ReadableMapprops) { mUIImplementation.createView(tag, className, rootViewTag, props); } ``` @ReactMethod注解表明这个是个供JS调用的java方法。所以每一个JS代码中写的控件最终都是通过这个入口createView创建一个个Android能够识别的View,具体的进一步代码实现如下: ```java public void createView(int tag, String className, int rootViewTag, ReadableMap props) { ReactShadowNode cssNode = createShadowNode(className); ReactShadowNode rootNode = mShadowNodeRegistry.getNode(rootViewTag); cssNode.setReactTag(tag); // Thread safety needed here cssNode.setViewClassName(className); cssNode.setRootTag(rootNode.getReactTag()); cssNode.setThemedContext(rootNode.getThemedContext()); mShadowNodeRegistry.addNode(cssNode); ReactStylesDiffMap styles = null; if (props != null) { //这里这里!!! ---元素的样式解析、赋值 styles = new ReactStylesDiffMap(props); cssNode.updateProperties(styles); } handleCreateView(cssNode, rootViewTag, styles); } } ``` 这一步主要是把样式和一些配置解析出来,方便下一步渲染。执行`handleCreateView`之后,会把这些信息转换成一个对象,放入队列里,等待执行。入队列的操作是在`NativeViewHierarchyOptimizer`类的`handleCreateView`方法里: ```java public void handleCreateView( mUIViewOperationQueue.enqueueCreateView( themedContext, node.getReactTag(), node.getViewClass(), initialProps); } ``` ```java public void enqueueCreateView( ThemedReactContext themedContext, int viewReactTag, String viewClassName, @Nullable ReactStylesDiffMap initialProps) { synchronized (mNonBatchedOperationsLock) { mCreateViewCount++; mNonBatchedOperations.addLast( new CreateViewOperation(themedContext, viewReactTag, viewClassName, initialProps)); } } ``` 可以看到这些操作其实是一些实现了*`UIOperation`*接口的类 查看*`UIOperation`*类可以看到这个类有个重要的方法:execute,即所有的这些操作需要执行execute方法才能真正变成一个个Android中的View所对应的类 用Android Studio查看`CreateViewOperation`的`execute`方法的调用时机,不难发现,该方法最终是在`UIManagerModule`类的`onBatchComplete`方法来调用的,当JNI C++那一层完成JS代码加载之后就会回调到该方法 ```java @Override public void onBatchComplete() { int batchId = mBatchId; mBatchId++; SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchCompleteUI") .arg("BatchId", batchId) .flush(); for (UIManagerModuleListener listener : mListeners) { listener.willDispatchViewUpdates(this); } for (UIManagerListener listener : mUIManagerListeners) { listener.willDispatchViewUpdates(this); } try { // If there are no RootViews registered, there will be no View updates to dispatch. // This is a hack to prevent this from being called when Fabric is used everywhere. // This should no longer be necessary in Bridgeless Mode. if (mNumRootViews > 0) { mUIImplementation.dispatchViewUpdates(batchId); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } } ``` 这里先记住有这么一个方法,不展开介绍,后续会提及 ## 2.5 NativeViewHierarchyManager.createView ```java public synchronized void createView( ThemedReactContext themedContext, int tag, String className, @Nullable ReactStylesDiffMap initialProps) { ViewManager viewManager = mViewManagers.get(className); View view = viewManager.createView(tag, themedContext, initialProps, null, mJSResponderHandler); } public @NonNull T createView( int reactTag, @NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap props, @Nullable StateWrapper stateWrapper, JSResponderHandler jsResponderHandler) { T view = createViewInstance(reactTag, reactContext, props, stateWrapper); if (view instanceof ReactInterceptingViewGroup) { ((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler); } return view; } protected abstract @NonNull T createViewInstance(@NonNull ThemedReactContext reactContext); protected @NonNull T createViewInstance( int reactTag, @NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap initialProps, @Nullable StateWrapper stateWrapper) { T view = createViewInstance(reactContext); view.setId(reactTag); addEventEmitters(reactContext, view); if (initialProps != null) { updateProperties(view, initialProps); } // Only present in Fabric; but always present in Fabric. if (stateWrapper != null) { Object extraData = updateState(view, initialProps, stateWrapper); if (extraData != null) { updateExtraData(view, extraData); } } return view; } ``` 代码中直接调用`ViewManager.createView`就直接一个个View就出来了,举个例子RCTText也就是JS端中的Text控件,由于每一个View都是对应一个`ViewManager`所以Text这个控件对应的就是`ReactTextViewManager`,而`ViewManager.createView`最后调用的就是`createViewInstance`方法 ```java public ReactTextView createViewInstance(ThemedReactContext context) { return new ReactTextView(context); } ``` # 3. 安卓渲染机制如何驱动RN进行渲染 ## 3.1 安卓渲染机制简介 最开始的起点是`Looper`类,这是一个用于为线程运行消息循环的类,配合`Choreographer`类来工作 每一个Looper线程都有自己的`Choreographer`,其他线程发送的回调只能运行在对应`Choreographer`所属的`Looper`线程上 ```java private Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mHandler = new FrameHandler(looper); // 根据是否使用了VSYNC来创建一个FrameDisplayEventReceiver对象 mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; // 是指上一次帧绘制时间点 mLastFrameTimeNanos = Long.MIN_VALUE; // 帧间时长,一般等于16.7ms mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; // CALLBACK_LAST + 1 = 4,创建一个容量为4的CallbackQueue数组,用来存放4种不同的Callback for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } // b/68769804: For low FPS experiments. setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1)); } ``` `Choreographer`类中有一个`Looper`和一个`FrameHandler`变量。变量`USE_VSYNC`用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。如果系统使用了Vsync同步机制,则创建一个`FrameDisplayEventReceiver`对象用于请求并接收Vsync事件,最后`Choreographer`创建了一个大小为3的`CallbackQueue`队列数组,用于保存不同类型的Callback。这里,不同类型的Callback包括如下4种: ```java public static final int CALLBACK_INPUT = 0; // 输入 public static final int CALLBACK_ANIMATION = 1; // 动画 public static final int CALLBACK_TRAVERSAL = 2; // 视图绘制 public static final int CALLBACK_COMMIT = 3; // 提交 ( 这一类型是在API level=23的时候添加的) ```  RN创建了自己的Callback来集成进安卓的渲染机制,进而驱动自身的渲染 ## 3.2 初始化 在通过builder创建`CatalystInstanceImpl`的过程中,会创建一个`ReactQueueConfigurationImpl`对象,在其工厂方法中,又创建了一个`MessageQueueThreadImpl`对象,其工厂方法又通过调用*`createForMainThread`*方法,创建了一个`MessageQueueThreadImpl`对象。该对象保存了一个`Looper`,然后创建了一个`MessageQueueThreadHandler`对象`。MessageQueueThreadHandler`对象继承了安卓的`Handler`类,具有分发`Message`的能力 > `Message`用来定义一条消息,其中包含可以发送到Handler的描述和任意数据对象,其拥有一个`Runnale`类型的callback变量 > `Handler` 允许发送和处理与线程的MessageQueue关联的`Message`和 `Runnable` 对象。每个 `Handler` 实例都与单个线程和该线程的消息队列相关联。当创建一个新的 `Handler` 时,它会绑定到Loo`p`er 。它将消息和`runnables` 传递到该`Looper` 的消息队列并在该`Looper` 的线程上执行它们 ## 3.3 集成 在`CatalystInstanceImpl`初始化了bridge之后,会回调到`MessageQueueThreadImpl`对象的`runOnQUeue`方法,后续所有的任务(runnable)都会交给该方法回调使用。通过debug可以知道它的参数是一个`NativeRunnable`,由于该对象是c++实现的,无法直接查看其源码,但通过debug不难发现,`NativeRunnable`正好会回调到前面提到的`UIManagerModule`类的`[onBatchComplete](https://www.notion.so/React-Native-6f08dc0ca5154296a999ed3c1f5a59e0)`方法该方法最后会调用`dispatchViewUpdates`方法,效果就是往一个`mDispatchUIRunnables`集合添加runnable元素,,看一下该runnable的定义: ```java Runnable runOperations = new Runnable() { @Override public void run() { SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "DispatchUI") .arg("BatchId", batchId) .flush(); try { long runStartTime = SystemClock.uptimeMillis(); // All ViewCommands should be executed first as a perf optimization. // This entire block is only executed if there's at least one ViewCommand queued. if (viewCommandOperations != null) { for (DispatchCommandViewOperation op : viewCommandOperations) { try { op.executeWithExceptions(); } catch (RetryableMountingLayerException e) { // Catch errors in DispatchCommands. We allow all commands to be retried // exactly once, after the current batch of other mountitems. If the second // attempt fails, then we log a soft error. This will still crash only in // debug. We do this because it is a ~relatively common pattern to dispatch a // command during render, for example, to scroll to the bottom of a ScrollView // in render. This dispatches the command before that View is even mounted. By // retrying once, we can still dispatch the vast majority of commands faster, // avoid errors, and still operate correctly for most commands even when // they're executed too soon. if (op.getRetries() == 0) { op.incrementRetries(); mViewCommandOperations.add(op); } else { // Retryable exceptions should be logged, but never crash in debug. ReactSoftExceptionLogger.logSoftException( TAG, new ReactNoCrashSoftException(e)); } } catch (Throwable e) { // Non-retryable exceptions should be logged in prod, and crash in Debug. ReactSoftExceptionLogger.logSoftException(TAG, e); } } } // All nonBatchedOperations should be executed before regular operations as // regular operations may depend on them if (nonBatchedOperations != null) { for (UIOperation op : nonBatchedOperations) { op.execute(); } } if (batchedOperations != null) { for (UIOperation op : batchedOperations) { op.execute(); } } } catch (Exception e) { mIsInIllegalUIState = true; throw e; } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } } }; ``` 看着是不是似曾相似?是的,前文提到的各种Operation,例如`[CreateViewOperation](https://www.notion.so/React-Native-6f08dc0ca5154296a999ed3c1f5a59e0)`就是在这里被最终调用的 首先在`ReactInstanceManager`中创建一个`ReactReactApplicationContext`,该方法中有一段逻辑是`reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable)`,其最终也会交给`ReactQueueConfigurationImpl`的`runOnQUeue`方法,后文相同的逻辑不再赘述。该runnable设置了`ReactContext`,经过一系列调用最终会到`ReactContext`的`addLifecycleEventListener`方法,该方法同样通过`runOnQUeue`回调到其传入的`runnable`逻辑,而该逻辑会在适当的时候触发。该逻辑有一个`listener.onHostResume`方法调用,`LifecycleEventListener`是一个接口,有很多的实现类,实际上在`ReactContext`的初始化过程中,上述逻辑会重复很多次,也就意味着有多个listener实现类都调用了`onHostResume`方法,其中比较重要的是`UIManagerModule`,其`onHostResume`方法又调用了`UIViewOperationQueue`的`resumeFrameCallback`方法: ```java public void onHostResume() { mOperationsQueue.resumeFrameCallback(); } /* package */ void resumeFrameCallback() { mIsDispatchUIFrameCallbackEnqueued = true; ReactChoreographer.getInstance() .postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback); } ``` 可以看到这里传入了一个`DispatchUIFrameCallback`,先不管该callback的作用是什么,先看这个`postFrameCallback`做了什么 ```java public void postFrameCallback( CallbackType type, ChoreographerCompat.FrameCallback frameCallback) { synchronized (mCallbackQueuesLock) { mCallbackQueues[type.getOrder()].addLast(frameCallback); mTotalCallbacks++; Assertions.assertCondition(mTotalCallbacks > 0); if (!mHasPostedCallback) { if (mChoreographer == null) { initializeChoreographer( new Runnable() { @Override public void run() { postFrameCallbackOnChoreographer(); } }); } else { postFrameCallbackOnChoreographer(); } } } } ``` 重点是第一行,把frameCallback放进了`mCallbackQueues`数组中,供后面拿出来使用 先不急着分析方法,先来看看`ReactChoreographer`类是什么,可以看出该类的名字与前文提到的`Choreographer`很相似,实际上该类就可以看成是`Choreographer`的代理类。看一下`ReactChoreographer`的构造方法: ```java private ReactChoreographer() { mReactChoreographerDispatcher = new ReactChoreographerDispatcher(); mCallbackQueues = new ArrayDeque[CallbackType.values().length]; for (int i = 0; i < mCallbackQueues.length; i++) { mCallbackQueues[i] = new ArrayDeque<>(); } initializeChoreographer(null); } ``` `ReactChoreographerDispatcher`类是一个实现了`ChoreographerCompat.FrameCallback`接口的类,该类后续会提及,现在不进行展开,先看`initializeChoreographer`的逻辑 ```java public void initializeChoreographer(@Nullable final Runnable runnable) { // 该方法与前面提到的ReactQueueConfigurationImpl的runOnQUeue方法逻辑类似,实际上底层是完全一样的,都是调用Handler的sendMessageAtTime方法,其结果都是会回调传入的runnable逻辑 UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { synchronized (ReactChoreographer.class) { if (mChoreographer == null) { mChoreographer = ChoreographerCompat.getInstance(); } } if (runnable != null) { runnable.run(); } } }); } ``` 接下来分析该Runnable做了什么事情,很简单,通过`ChoreographerCompat`的工厂方法创建了一个对象,看一下逻辑 ```java public static ChoreographerCompat getInstance() { UiThreadUtil.assertOnUiThread(); if (sInstance == null) { sInstance = new ChoreographerCompat(); } return sInstance; } private ChoreographerCompat() { mChoreographer = getChoreographer(); } private Choreographer getChoreographer() { return Choreographer.getInstance(); } ``` 其效果就是创建了一个`ChoreographerCompat`对象,并在内部保存了一个`Choreographer`的引用,因此这一论操作下来的结果就可以看成是`ReactChoreographer`类对`Choreographer`的代理。接下来回到原来的[主线](https://www.notion.so/React-Native-6f08dc0ca5154296a999ed3c1f5a59e0),看`ReactChoreographer`的`postFrameCallback`方法逻辑,这里把代码重新放一遍 ```java public void postFrameCallback( CallbackType type, ChoreographerCompat.FrameCallback frameCallback) { synchronized (mCallbackQueuesLock) { mCallbackQueues[type.getOrder()].addLast(frameCallback); mTotalCallbacks++; Assertions.assertCondition(mTotalCallbacks > 0); if (!mHasPostedCallback) { if (mChoreographer == null) { initializeChoreographer( new Runnable() { @Override public void run() { postFrameCallbackOnChoreographer(); } }); } else { postFrameCallbackOnChoreographer(); } } } } private void postFrameCallbackOnChoreographer() { mChoreographer.postFrameCallback(mReactChoreographerDispatcher); mHasPostedCallback = true; } ``` 最终会调用到`ChoreographerCompat`的`postFrameCallback`方法 ```java public void postFrameCallback(FrameCallback callbackWrapper) { choreographerPostFrameCallback(callbackWrapper.getFrameCallback()); } ``` 同样先不看逻辑,先看类的定义。`FrameCallback` 是一个抽象类,也就意味着它有很多的子类 ```java public abstract static class FrameCallback { private Runnable mRunnable; private Choreographer.FrameCallback mFrameCallback; Choreographer.FrameCallback getFrameCallback() { if (mFrameCallback == null) { mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { FrameCallback.this.doFrame(frameTimeNanos); } }; } return mFrameCallback; } Runnable getRunnable() { if (mRunnable == null) { mRunnable = new Runnable() { @Override public void run() { doFrame(System.nanoTime()); } }; } return mRunnable; } /** * Just a wrapper for frame callback, see {@link * android.view.Choreographer.FrameCallback#doFrame(long)}. */ public abstract void doFrame(long frameTimeNanos); } ``` 当调用它的`getFrameCallback`方法,它同样会返回一个`FrameCallback` 类,但注意这里有两个`FrameCallback` 类,这里返回的是`Choreographer.FrameCallback`,是属于安卓的而非RN的。`postFrameCallback`方法最终会调用`Handler`的`sendMessageAtTime`方法,因此直接看内部的逻辑,`FrameCallback.this.doFrame`,这行代码意味着调用子类的doFrame方法,因为其本身是一个抽象类,该方法是一个抽象方法,需要由子类去实现。 具体的来讲,这里传入的`mReactChoreographerDispatcher`就是`ReactChoreographerDispatcher`类型,前面有[提及](https://www.notion.so/React-Native-6f08dc0ca5154296a999ed3c1f5a59e0),看它的doFrame方法: ``` @Override public void doFrame(long frameTimeNanos) { synchronized (mCallbackQueuesLock) { mHasPostedCallback = false; for (int i = 0; i < mCallbackQueues.length; i++) { ArrayDeque<ChoreographerCompat.FrameCallback> callbackQueue = mCallbackQueues[i]; int initialLength = callbackQueue.size(); for (int callback = 0; callback < initialLength; callback++) { ChoreographerCompat.FrameCallback frameCallback = callbackQueue.pollFirst(); if (frameCallback != null) { frameCallback.doFrame(frameTimeNanos); mTotalCallbacks--; } else { FLog.e(ReactConstants.TAG, "Tried to execute non-existent frame callback"); } } } maybeRemoveFrameCallback(); } } ``` 可以看出来,这里就把刚刚放进数组的callback拿出来调用了,其中一个就包含又刚刚提到的其中一个类型为`DispatchUIFrameCallback`的callback,看看他的doFrame方法: ```java @Override public void doFrameGuarded(long frameTimeNanos) { if (mIsInIllegalUIState) { FLog.w( ReactConstants.TAG, "Not flushing pending UI operations because of previously thrown Exception"); return; } Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations"); try { dispatchPendingNonBatchedOperations(frameTimeNanos); } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } flushPendingBatches(); ReactChoreographer.getInstance() .postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, this); } ``` 重点是`flushPendingBatches`方法: ```java private void flushPendingBatches() { final ArrayList<Runnable> runnables; synchronized (mDispatchRunnablesLock) { if (!mDispatchUIRunnables.isEmpty()) { runnables = mDispatchUIRunnables; mDispatchUIRunnables = new ArrayList<>(); } else { return; } } for (Runnable runnable : runnables) { runnable.run(); } } ``` 这里看到了`mDispatchUIRunnables`集合,还有印象吗?就是[前面](https://www.notion.so/React-Native-6f08dc0ca5154296a999ed3c1f5a59e0)存放Operation的集合,在这里被实际调用了!至此RN的组件被实际创建出来了,但实际上RN内部的机制非常复杂,还有诸如`DispatchUIFrameCallback`的非常多的callback和诸如`UIManagerModule`的非常多的listener,这两个只是其中的两种内部机制,所有的机制配合起来才实现了RN组件的创建和渲染 ## 3.4 实际渲染 todo 最后修改:2022 年 06 月 12 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏
1 条评论
您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。