React Native 底层原理与实现流程深度解析

引言

React Native 是 Facebook 开发的一个开源框架,允许开发者使用 JavaScript 和 React 构建跨平台移动应用。它承诺"一次学习,随处编写"(Learn once, write anywhere),让 Web 开发者能够利用现有技能开发移动应用,同时保持原生应用的性能和用户体验。本报告将深入探讨 React Native 的底层原理、实现流程和调用逻辑,帮助开发者更全面地理解这一框架的工作机制。

React Native 的整体架构

React Native 的整体架构可以分为三个主要部分:JS域、Native域以及负责两个域之间通信的C++ Bridge。这一架构设计使得开发者可以使用JavaScript编写业务逻辑,同时利用原生组件实现高性能的UI渲染。

三层架构设计

React Native 的核心架构可以概括为三层结构:

  • JS层:运行在JavaScriptCore上的JavaScript代码,处理业务逻辑和组件渲染

  • Shadow层:包含React Native的核心逻辑和组件定义

  • Native层:平台相关的原生代码,负责实际的UI渲染和系统交互

这种分层设计使得React Native能够同时保持开发效率和运行性能[11]。

React Native 的主要特点

React Native 具有以下显著特点:

  1. 跨平台开发:使用 Virtual DOM 技术,只需编写一套代码,就可以将应用打包为不同平台的App,大幅提高开发效率[49]。

  2. 性能优化:由于 React Native 使用的组件是对原生 API 的封装,体验和性能足以媲美原生应用[49]。

  3. 热更新能力:React Native 的产物是 bundle 文件,本质上是 JS 代码,应用启动时会从服务器获取最新的 bundle 文件,实现无需重新下载应用即可更新功能[49]。

  4. 开发效率高:相比原生开发,JavaScript 学习成本低、语法灵活,允许 Web 开发者更多地基于现有经验开发 App[46]。

React Native 的启动流程

React Native 的启动流程是理解其工作原理的关键。以下是 React Native 启动的主要步骤:

创建 ReactRootView

ReactRootView 是 React Native 的根视图,负责监听标准 Android 的 View 相关各种事件及事件分发和子 View 渲染等。在创建 ReactRootView 时,会初始化 ReactInstanceManager 和 ReactContext 实例[36]。

创建 ReactInstanceManager

ReactInstanceManager 是大内总管接口类,提供一个构造者模式的初始化 Builder。它是 React Native 启动流程的核心入口,负责管理 JS 和 Native 交互,做中转角色[36]。

创建 ReactContext

ReactContext 是 React Native 的上下文对象,包含 ReactInstanceManager 创建的 NativeModuleRegistry 和 JavaScriptModuleRegistry 映射表。ReactContext 在 ReactInstanceManager 创建后被关联到 ReactInstanceManager 实例上[36]。

加载 JS Bundle

React Native 会加载打包好的 JS Bundle 文件,这个文件包含了应用的业务逻辑和组件定义。加载完成后,通过调用 AppRegistry.runApplication 方法启动应用[36]。

渲染组件

在启动完成后,React Native 会根据注册的组件进行渲染。组件会被包裹在 AppContainer 中,AppContainer 用于外面包围一些 Debug 用的工具(如红盒)[50]。

React Native 的通信机制

React Native 的核心在于其跨语言通信能力,它通过 Bridge 进行原生端和 JavaScript 之间的交互。以下是 React Native 的通信机制详解:

Bridge 架构

在 React Native 中,原生端和 JavaScript 交互是通过 Bridge 进行的。Bridge 的作用是给 React Native 内嵌的 JS Engine 提供原生接口的扩展供 JS 调用。所有的本地存储、图片资源访问、图形图像绘制、3D 加速、网络访问、震动效果、NFC、原生控件绘制、地图、定位、通知等都是通过 Bridge 封装成 JS 接口以后注入 JS Engine 供 JS 调用[46]。

Native 调用 JS

在 React Native 里面,JS 的方法可以通过 global.batchedBridge.callFunctionReturnFlushedQueue 这个方法进行调用。在 Native 侧,只需将 React Native 里面的 global.batchedBridge 对象中的方法和 Native 侧的 JSIExecutor 方法进行绑定(本质上 Native 指针指向 JS 函数)[46]。

Native 侧的 callFunctionReturnFlushedQueue 主要做了以下事情:

  • 通过 moduleid 和 methodid 完成方法的调用,通过这两个参数可以找到 JS 侧定义的方法模块。

JS 调用 Native

当 JS 调用 Native 模块的时候,会调用一个 Native 暴露出来的全局方法:nativeFlushQueueImmediate,并通过传入要调用的 moduleName、methodName、callback 参数给这个方法,然后这个方法再通知给 Native 侧找到相应的模块并执行[46]。

React Native 中的线程模型

React Native Android 端主要有三个线程:

  1. main_ui 线程:负责 UI 渲染

  2. mqt_js 线程:负责执行 JS 代码

  3. mqt_native_modules 线程:与 NativeModule 相关

每个线程都有与其绑定的消息队列。native 侧 (main_ui 和 mqt_native_modules 线程) 通过调用 jniCallJSFunction 和 jniCallJSCallback 方法来执行 JS 侧的代码,从而实现与 mqt_js 线程交互;mqt_js 线程通过调用 NativeModule 暴露给 JS 侧的方法与 mqt_native_modules 线程交互;而 main_ui 与 mqt_native_modules 可通过 Handler 直接交互[51]。

React Native 的虚拟 DOM 机制

React Native 使用虚拟 DOM 来提高渲染效率,这与 React 在 Web 上的实现类似。

虚拟 DOM 的概念

虚拟 DOM 是一个轻量级的 JavaScript 对象,表示 DOM 树的结构。它具有平台无关性:它描述的 UI 控件只是数据结构层的,具体渲染工作是交给了原生渲染引擎(浏览器、iOS、Android)去处理[46]。

React Native 中的虚拟 DOM

在 React Native 里面,用来表示 dom 属性的对象有以下关键属性:

javascript

复制

解释
var ele = {
  ... type: type, // 元素的类型 key: key, // 元素key标示 ref: ref, // 元素的引用 props: props, // 元素的参数,包含children 
  ... 
}

React 里面的虚拟 DOM 把真实 DOM 分为了以下几种类型:

  • 原子类型:类型为字符串,结构上不可再分解,渲染由平台底层支持。

  • 组合类型:类型为函数构造器,它给我们提供了一种自定义元素 UI 和行为的能力。

渲染过程

React Native 的渲染过程大致如下:

  1. 将虚拟 DOM 转换为平台特定的原生控件

  2. 通过 UI Manager 创建和更新原生控件

  3. 利用 Yoga 布局引擎进行布局计算

  4. 将计算结果传递给原生层进行实际渲染

在 React Native 里面,是通过 UI Manager 来创建视图的,基于 Virtual DOM,React Native 把不同平台创建视图的逻辑封装了一层,不同平台通过 Bridge 调用 UI Manager 来创建不同的 Native 视图[46]。

React Native 的性能优化

React Native 的性能优化是开发者关注的重点,以下是几个关键点:

跨语言通信的性能问题

性能是我们考量一个框架或一套技术方案的重要指标之一。相对于 Java、OC 等编程语言,一直以来 JS 都留给我们执行慢的印象,其实随着 JS 语言的发展以及 JS 引擎的不断优化,现代 JS 代码的执行速度已经非常快了。

性能问题一般不会出现在 JS 域和 native 域,而往往会出现在 C++ Bridge 上。这是因为跨语言通信需要进行数据的序列化与反序列化,而这个过程是非常耗时的[51]。

UI 的异步更新

React Native 中的 UI 更新是异步完成的。JS 侧的更新指令被批量、异步地发送到 native 侧,在 native 执行实际的更新操作时,JS 线程不会被阻塞。React 为了解决 Web 上的类似问题,提出了虚拟 DOM 的概念,结合一个智能的 diff 算法,将我们在 JS 侧对组件的更改批量、异步地发送到 native 侧–同时页最小化了需要通过 Bridge 传递的数据量(diff 算法)[51]。

Hermes 引擎

Hermes 是 Facebook 在 2019 年发布的新一代 JS Engine,Hermes 是一款小巧轻便的 JavaScript 引擎,专门针对在 Android 上运行 React Native 进行了优化:应用启动时间减少、减少内存使用量并缩小应用程序大小,此外因为它采用 JavaScript 标准实现,所以很容易在 React Native 应用中集成[46]。

React Native 的组件系统

React Native 的组件系统是其核心功能之一,理解组件如何创建和渲染对于掌握 React Native 至关重要。

元组件与复合组件

从一定角度上来说,React 的组件可以分为两种:

  1. 元组件:框架内置的,可以直接用的组件。在 RN 上可以理解为就是 View/Image 这种。不同平台有不同的元组件实现。

  2. 复合组件:用户封装之后的组件,一般可以通过 React.createClass 来构建,提供 render() 方法返回渲染目标(ES6 中可以继承 React.Component)。

在 React 核心库中提供了 instantiateReactComponent.js,供渲染平台调用。它在碰见 ReactElement 时会根据其中的 type 生成元组件或者复合组件,逻辑如下[50]:

  1. 当对象的 type 如果是 string 或 function 时走第一层逻辑。当 type 为 string 时,渲染平台可以通过 ReactHostComponent.injectGenericNameClass 这个 API 来注入生成组件逻辑;若 type 函数的原型链中具有元组件 API 时,则 new 一个 type 实例;否则就生成一个 ReactCompositeComponent(即复合组件)。

  2. 当对象为 string 时(此种情况对应直接写 string,而不是用 jsx 包裹起来的语法),也交由渲染平台处理,可以通过 ReactHostComponent.injectTextComponentClass 来注入组件生成逻辑;

  3. 以上都不符合,则报错。

元组件的实现

React Native 的元组件通常是一个复合组件,但是它的 render 方法返回的是一个元组件。例如,View 组件的代码在 Libraries/Components/View/View.js 下,其实是一个复合组件,但是它的 render 方法返回的是一个元组件:

javascript

复制

解释
// View.jsconst View = React.createClass({// 属性声明...render: function() {return <RCTView {...this.props} />;
  },
});

const RCTView = requireNativeComponent('RCTView',View,
  {nativeOnly: {nativeBackgroundAndroid: true,nativeForegroundAndroid: true,
    },
  }
);

requireNativeComponent 做的事情是获取 UI Manager 中的对应 ViewModule 配置,并创建一个 ReactNativeBaseComponent 的构造函数[50]。

React Native 的新架构

React Native 在不断发展,其新架构带来了性能和开发体验的显著提升。

JSI(JavaScript Interface)

在 RN 中,JSI 是 JavaScript Interface 的缩写,JSI 是一个轻量级的通用的 API 框架,可以应用于任意的 JavaScript virtual machine,让各种平台可以方便地使用不同的 JavaScript 解析引擎(JavaScript virtual machine 包含 JavaScript Engine)。

JSI 是用 C++ 写的,用于取代原先的 bridge,提高通信效率,已在 RN 的 0.58 中实现。有了 JSI,我们就能轻松地直接调用原生 UI Views 或 Native Modules 用 Java/ObjC 实现的方法(类似 RPC),而不是像原来那样用一层 bridge 来排队等待原生层返回的消息[46]。

新架构的优势

JSI 本身不是 React Native 的一部分——它是一个统一的、轻量的、通用适用于任何(理论上)JavaScript 虚拟机的接口层。当把 JSI 加入到新架构中后,它使得一些真正重要的改进成为可能:

  1. 第一个改进很直观——javaScriptCore 现在可以更容易地被替换成其它引擎,其它选项包括微软的 ChakraCore 和谷歌的 V8。

  2. 第二个改进,可以说是整个新架构的基石,是通过使用 JSI,JavaScript 可以持有对 C++ 宿主对象的引用,并且对它进行调用。这意味着:JavaScript 和 Native 之间真正地相互知晓,并且不再需要通过 JSON 序列化传递消息,这会消除 Bridge 的阻塞问题[46]。

React Native 的热更新机制

热更新是 React Native 的一个关键特性,它允许开发者在不重新发布应用的情况下更新应用的功能。

热更新原理

React Native 的产物 bundle 文件,本质上是 JS 逻辑代码加上 React Native 的 Runtime 的集合,所以在应用一启动的时候就会去获取 bundle 文件,之后解析 bundle 文件,最后再由 JS Engine 去执行具体的业务代码逻辑。这就可以允许开发者在云端去更新 bundle 文件,然后应用启动的时候获取最新的 bundle 文件,这一整个流程下来就实现了热更新[46]。

增量更新(拆包)

对于 React Native 的代码打包之后只会生成一个 Bundle 文件,这里面包含了基础业务逻辑、React Native 的基础库类,所以我们可以把一个包拆分成:一个基础包+ n 个业务包,其中基础包是不变的,这就是 runtime,业务包就是具体的业务,后面如果有更新,也只需要再打出一个业务包就行[46]。

React Native 与 Flutter 的比较

React Native 和 Flutter 都是用于跨平台开发的框架,但它们在实现方式和使用体验上有显著差异。

开发体验比较

React Native 在界面开发延续了 React 开发风格,支持 css-in-js(其实就是用 js 来写 css),而且在 0.59 版本之后支持了 React Hook 函数式编程,开发的时候大多只关心样式界面的搭建,原生能力有客户端或者 Bridge 实现。

Flutter 最大的特点在于:Flutter 是一套平台无关的 UI 框架,并且在 Flutter 里面万物皆 Widget。很多时候开发一个控件需要嵌套多个 Widget 去实现,与 JS 里面的回调地狱有点像,而这也是被吐槽代码嵌套样式难看的原因[46]。

状态管理比较

React Native 和 Flutter 对于状态管理,两者有着很高的相似度,虽然内部实现有很大差别,但是都可以获取 state 和 setState 的方式去更新页面的状态。

产物比较

React Native 产生的是 bundle 文件,实际上就是 JS 脚本文件;而 Flutter 编译后 Android 产生的主要是一些应用程序指令段、数据段,虚拟机数据段、指令段,iOS 则是 App.framework,其实也是一些原生的数据集。

原生能力与性能比较

其实两者的在这方面的区别不是很大,性能方面 React Native 稍微差一点。但是在原生灵活性上 React Native 要有优势[46]。

结论

React Native 作为一种跨平台开发框架,通过其独特的三层架构设计(JS层、Shadow层、Native层)和高效的通信机制,实现了"一次学习,随处编写"的开发理念。其虚拟 DOM 机制和异步更新策略大大提高了渲染效率,而热更新功能则为应用迭代提供了便利。

随着 React Native 不断发展,其新架构引入的 JSI 技术进一步优化了跨语言通信效率,使 JavaScript 和 Native 之间能够更直接地交互,消除了 Bridge 的阻塞问题。同时,React Native 与 Flutter 等其他跨平台框架的比较显示,React Native 在开发体验和原生灵活性方面具有独特优势。

理解 React Native 的底层原理和实现流程,对于开发者优化应用性能、解决开发问题、甚至参与框架改进都具有重要意义。通过本报告的深入分析,我们希望读者能够对 React Native 的工作原理有更全面的了解,从而在实际开发中更加得心应手。

参考资料

[11] React Native 架构详解. https://buhe.gitbooks.io/react-native-primer-pro/content/rn_arch/1.html.

[36] React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信). https://blog.csdn.net/yanbober/article/details/53157456.

[46] React Native 原理与实践. https://zhuanlan.zhihu.com/p/343519887.

[49] 我对 React 实现原理的理解. https://zhuanlan.zhihu.com/p/538096803.

[50] React Native 核心渲染流程分析(1) - 初识组件系统. https://zhuanlan.zhihu.com/p/24834341.

[51] ReactNative设计与实现之三:整体架构. https://zhuanlan.zhihu.com/p/45836822.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容