
ReactNative渲染原理浅析.docx
12页 ReactNative渲染原理浅析 众所周知,RN和H5的区别在于:RN是使用Native组件来渲染的,而H5是依赖WebView那么RN是如何做到写js代码,渲染Native组件的呢,这篇文章我们深入源码,一探究竟使用的RN版本是v0.62.0JS侧的UI是使用React来实现的熟悉React的同学,都知道React使用jsx来写布局,然后会转换成虚拟dom树,最后再渲染到浏览器的真实dom里,那React Native是怎么做的呢?为了方便阅读,这里先把文中提到的一些函数列出来:一、启动以默认的demo为例,我们的代码入口在App.js,并且导出了jsx渲染函数const App = () => { return (
import {AppRegistry} from react-native;import App from ./App;import {name as appName} from ./app.json;AppRegistry.registerComponent(appName, () => App);看下注册函数:registerComponent( appKey: string, componentProvider: ComponentProvider, section?: boolean,): string { let scopedPerformanceLogger = createPerformanceLogger(); //存在runnales里 runnables[appKey] = { componentProvider, run: appParameters => { //运行run的时候,开始执行渲染 renderApplication( componentProviderInstrumentationHook( componentProvider, scopedPerformanceLogger, .....//省略 ) ); }, }; .....//省略 return appKey;},这里把渲染函数存在runnables对象里。
那么,是什么时候开始执行这里注册的runnables呢?这里就和客户端的调用有关了在启动RN页面时,客户端内部会调用下面这行代码,调用runApplication,传入对应的appName和一些参数catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);然后在js里AppRegistry.js就会执行对应名称的注册函数:runApplication(appKey: string, appParameters: any): void { .....//省略 runnables[appKey].run(appParameters);},可以看到走到了renderApplication里执行渲染逻辑再跟进去会发现有两个选择,ReactFabric和ReactNative,Fabric就是RN的新架构,现在最新的代码还是用的ReactNative: GlobalPerformanceLogger.startTimespan(renderApplication_React_render);//渲染计时开始 if (fabric) { require(../Renderer/shims/ReactFabric).render(renderable, rootTag);//令人期待的新框架Fabric } else { require(../Renderer/shims/ReactNative).render(renderable, rootTag); } GlobalPerformanceLogger.stopTimespan(renderApplication_React_render);//渲染计时结束二、遍历虚拟dom接下来的渲染逻辑就和React非常相关了。
其实就是React的代码进入Render函数:render: function(element, containerTag, callback) { var root = roots.get(containerTag); if (!root) { root = createContainer(containerTag, LegacyRoot, false, null); roots.set(containerTag, root); } updateContainer(element, root, null, callback); //进入这里继续执行渲染 return getPublicRootInstance(root);}在updateConainer里会创建一个update,并且插入到队列里,然后执行队列,接下来就是对组件树的遍历了var update = createUpdate(expirationTime, suspenseConfig);update.payload = { element: element};enqueueUpdate(current$$1, update);scheduleWork(current$$1, expirationTime);接下来就是不停地检查、插入队列、根据优先级处理(但这里是串行的,并没有异步),这里省略具体代码,依次的函数调用顺序如下:updateContainerscheduleUpdateOnFiberflushSyncCallbackQueueflushSyncCallbackQueueImplrunWithPriorityperformSyncWorkOnRootworkLoopSync在workLoopSync里,我们可以看到一个while循环,这里就开始遍历组件树了function workLoopSync() { while (workInProgress !== null) { workInProgress = performUnitOfWork(workInProgress); }}react遍历树的时候有两个重要的函数performUnitOfWork和completeUnitOfWork。
我理解performUnitOfWork就是深度遍历到底,然后执行completeUnitOfWork回退,同时创建对应的dom/Native组件也就是先创建的子节点再创建父节点的看下performUnitOfWork的部分代码:function performUnitOfWork(unitOfWork) { //开始处理,会返回子组件fiber实例,用于深度循环遍历,把任务加入队列 next = beginWork$$1(current$$1, unitOfWork, renderExpirationTime); if (next === null) { // 不存在子级fiber,完成当前单元任务的处理 next = completeUnitOfWork(unitOfWork); } return next;}这样就完成了一个子集任务的内容在completeUnitOfWork里,主要就是找父组件回退、找兄弟组件继续遍历: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的一个重要设计思路可以避免一次渲染大量组件而阻塞了线程导致用户操作没有响应更多关于组件Fiber链表和遍历的介绍可以看这个文章三、创建Native组件创建组件是在completeWork里完成的里面有很多不同类型的组件里面涉及创建真实渲染的Dom或Native组件的是HostComponent,这个组件最后会调用createInstance来创建组件激动人心,终于要创建组件了!function createInstance() { var updatePayload = create(props, viewConfig.validAttributes); ReactNativePrivateInterface.UIManager.createView( tag, // reactTag viewConfig.uiViewClassName, // viewName rootContainerInstance, // rootTag updatePayload // props ); var component = new ReactNativeFiberHostComponent(tag, viewConfig); return component;}这里主要是调用了UIManager的createView方法,传入了tag、viewName、rootTag、props参数信息。
这里的UIManager实际上是映射到Java里的一个class-。





![河南新冠肺炎文件-豫建科[2020]63号+豫建科〔2019〕282号](http://img.jinchutou.com/static_www/Images/s.gif)






