说明

RN需要一个JS的运行环境, 在IOS上直接使用内置的javascriptcore, 在Android 则使用webkit.org官方开源的jsc.so。 此外还集成了其他开源组件,如fresco图片组件,okhttp网络组件等。

RN 会把应用的JS代码(包括依赖的framework)编译成一个js文件(一般命名为index.android.bundle), , RN的整体框架目标就是为了解释运行这个js 脚本文件,如果是js 扩展的API, 则直接通过bridge调用native方法; 如果是UI界面, 则映射到virtual DOM这个虚拟的JS数据结构中,通过bridge 传递到native , 然后根据数据属性设置各个对应的真实native的View。

重要术语:

JavaScriptCore(JSC):JavaScript引擎,用于执行JS代码。
Yoga:UI引擎,用于计算元素在用户屏幕上展示的位置。
JSI:JavaScript Interface

架构分析

老架构


Bridge三个特点:

  • 异步。这些消息队列是异步的,无法保证处理事件。
  • 序列化。通过JSON格式来传递消息,每次都要经历序列化和反序列化,开销很大。
  • 批处理。对Native调用进行排队,批量处理。

异步设计的好处是不阻塞,这种设计在大部分情况下性能满足需求,但是在某些情况下就会出问题,比如瀑布流滚动。

当瀑布流向下滑动的时候,需要发请求给服务端拿数据进行下一步渲染。

滚动事件发生在UI thread,然后通过Bridge发给JS thread。JS thread 监听到消息后发请求,服务端返回数据,再通过Bridge返回给Native进行渲染。由于都是异步,就会出现空白模块,导致性能问题。

目前 RN 使用 Bridge Module 来让 JS 和 Native 线程进行通信,每次利用 Bridge 发送数据时,都需要转换为 JSON, 而收到数据时也需要进行解码。

这就意味着 JavaScript 和 Native 直接是隔离的,也就是 JS 线程不能直接调用 Native 线程上的方法。

新架构

FB团队在2018年提出了新架构:
在全新架构中,Bridge 将被一个名为 JavaScript Interface 的模块所代替,它是一个轻量级的通用层,用 C++ 编写,JavaScript Engine 可以使用它直接执行或者调用 native。

通用层代表着:JSI 让 JavaScript 接口将与 Engine 分离,这意味着新架构支持 RN 直接使用其他 JavaScript 引擎,比如 Chakra、v8、Hermes 等等。。
当前的架构使用的是JavaScriptCore引擎,bridge只兼容该引擎。而JSI并非如此,它将JavaScript接口与引擎解耦,这意味着新架构可以使用其他JavaScript引擎,如Chakra,v8,Hermes等,因此它是“通用”的。


主要有JSI、Fabric、TurboModules、CodeGen、LeanCode组成。

JSI

JSI是Javascript Interface的缩写,一个用C++写成的轻量级框架,它作用就是通过JSI,JS对象可以直接获得C++对象(Host Objects)引用,并调用对应方法。

  • JSI 将支持其他 JS 引擎;
  • JSI 允许线程之间的同步相互执行,不需要 JSON 序列号等耗费性能的操作;
  • JSI 是用 C++ 编写,以后如果针对电视、手表等其他系统,也可以很方便地移植;

Fabric

Fabric 是新的渲染系统,它将取代当前的 UI Manager。
UI Manager:
当 App 运行时,React 会执行你的代码并在 JS 中创建一个 ReactElementTree ,基于这棵树渲染器会在 C++ 中创建一个 ReactShadowTree。UI Manager 会使用 Shadow Tree 来计算 UI 元素的位置,而一旦 Layout 完成,Shadow Tree 就会被转换为由 Native Elements 组成的 HostViewTree(例如:RN 里的 <View/> 会变成 Android 中的 ViewGroup 和 iOS 中的 UIView)。

而之前线程之间的通信都发生在 Bridge 上,这就意味着需要在传输和数据复制上耗费时间。通过JSON格式来传递消息,每次都要经历序列化和反序列化。
而得益于前面的 JSI, JS 可以直接调用 Native 方法,其实就包括了 UI 方法,所以 JS 和 UI 线程可以同步执行从而提高列表、跳转、手势处理等的性能。

Turbo Modules

在之前的架构中 JS 使用的所有 Native Modules(例如蓝牙、地理位置、文件存储等)都必须在应用程序打开之前进行初始化,这意味着即使用户不需要某些模块,但是它仍然必须在启动时进行初始化。

Turbo Modules 基本上是对这些旧的 Native 模块的增强,正如在前面介绍的那样,现在 JS 将能够持有这些模块的引用,所以 JS 代码可以仅在需要时才加载对应模块,这样可以将显着缩短 RN 应用的启动时间。

Codegen

Fabric和Turbo Modules听起来很有前途,但是JavaScript是一门动态语言,而JSI是用C++写的,C++是一门静态语言,因此需要保证两者间的顺利通信。

这就是新架构还包括一个名为CodeGen的静态类型检查器的原因。

CodeGen使用类型确定后的JavaScript来为Turbo Modules和Fabric定义供他们使用的接口元素,并且它会在构建时生成更多的native代码,而非运行时。

skia

众所周知,Flutter 跨平台的性能提升和解耦来自于直接使用 Skia 渲染而非系统控件,而如今 RN 也有类似的支持。

react-native-skia 需要 react-native@>=0.66 的支持,而目前它上面的操作都还是十分原始的 canvas 行为,例如通过 Circle 绘制圆形,通过 blendMode 配置重叠模式等。

可以预见目前的 react-native-skia 还有不少问题需要解决,但是它让 RN 可以更高效地使用丰富的 Canvas 能力,对于 RN 的未来而言不免是一次不错的尝试。

主要亮点总结

  • Bridge会被JSI取代
  • 可以用其他引擎替代JavaScriptCore
  • 所有线程间可以完全互相操作
  • Web式的渲染系统
  • 对时间敏感的任务可以同步执行
  • Turbo Modules实现模块懒加载
  • JS端和Native端的静态类型检查

RN的三个线程

1、Main Thread

主线程又称 UI thread,主要负责 UI 的渲染以及用户行为的监听等等,是 App 启动时首先创建的线程

2、JavaScript Thread

通过 JavaScript Core 或者是 Hermes 引擎,JavaScript 代码的解析和执行是由 JS 线程负责的。和浏览器环境不同的是, JS 代码解析和执行在独立 JS 线程,而不是和 UI 渲染共用一个线程。

3、Shadow Thread

前两个线程都比较好理解,那 Shadow 线程是做什么的呢?要回答这个问题,首先我们需要理解 React 的原理。

我们都知道在浏览器环境 React DOM 会维护一个 Virtual DOM,Virtual DOM 实际上是在内存中的一个树形结构(Tree-Like)的 JS Object,用来映射真实的 DOM Node Tree。每次 State 更新后,React 会生成一个新的 Virtual DOM,通过和旧的 virtual DOM 做对比(Diffing),从而仅仅将需要变更的部分同步到真实 DOM(Reconciliation),从而减少 Dom Tree 的变更,从而减少浏览器的绘制工作。

与之类似的是,在 React Native,React 也会维护一个类似的 “Virtual DOM”,也会有 Diffing 的过程。但是不同于浏览器环境中 React DOM 直接调用浏览器 API 来完成真正的 DOM 更新操作,Native 环境下 React Native 是通过 Bridge ,将需要变更的指令(Commands)以字符串的方式发送到 Native Side,而对应在 Native 这边负责处理这些指令的进程就是 Shadow Thread。

Shadow Thread 通过维护一个 Shadow Tree 来计算 “Virtual DOM” 在 Native 页面的实际布局,然后通过 Bridge 异步通知 Main Thread 渲染 UI。

Shadow Tree 可以理解为是 “Virtual DOM” 在 Native 的映射,拥有和 Virtual DOM 相同的树形层级关系

RN是如何渲染的

将 React 代码渲染到宿主平台,我们称为渲染流水线,可大致分为三个阶段:

  • 渲染(Render):在 JavaScript 中,React 执行那些产品逻辑代码创建 React 元素树(React Element Trees)。然后在 C++ 中,用 React 元素树创建 React 影子树(React Shadow Tree)。
  • 提交(Commit):在 React 影子树完全创建后,渲染器会触发一次提交。这会将 React 元素树和新创建的 React 影子树的提升为“下一棵要挂载的树”。 这个过程中也包括了布局信息计算。
  • 挂载(Mount):React 影子树有了布局计算结果后,它会被转化为一个宿主视图树(Host View Tree)。

渲染流水线存在三种不同场景:

  • 初始化渲染
  • React 状态更新
  • React Native 渲染器的状态更新

场景1:初始化渲染

例如要渲染该组件

function MyComponent() {return (<View><Text>Hello, World</Text></View>);
}// <MyComponent />

1、render phase

  • JS 线程运行,Virtual DOM Tree 被创建
  • JS 线程通知 Shadow Thread 创建 Shadow Tree

    2、commit phase
  • 布局计算(Layout Calculation):通过 Yoga 布局引擎来计算每个 React 影子节点的位置和大小
  • 树提升,从新树到下一棵树(Tree Promotion,New Tree → Next Tree):这次提升代表着这个树是最新的,可以在下一次cpu轮转的时候挂载

3、mount phase
视图挂载(View Mounting):这个步骤会在对应的原生视图上执行原子变更操作,该步骤是发生在原生平台的 UI 线程的。

场景2:React 状态更新

https://www.react-native.cn/docs/render-pipeline
根据上面的渲染得到了下面的树

我们准备把3红色的背景变为黄色:
1、render phase

3要变化,所以复制了1,2,3。没有复制 4
2、commit phase

  • 布局计算(Layout Calculation):通过 Yoga 布局引擎来计算每个 React 影子节点的位置和大小
  • 树提升,从新树到下一棵树(Tree Promotion,New Tree → Next Tree):这次提升代表着这个树是最新的,可以在下一次cpu轮转的时候挂载
  • 树对比(Tree Diffing): 这个步骤会计算“先前渲染的树”(T)和“下一棵树”(T’)的区别。计算的结果是原生视图的变更操作。
    在上面的例子中,这些操作包括:UpdateView(**'Node 3'**, {backgroundColor: 'yellow'})

3、mount phase
视图挂载(View Mounting):同场景1

场景3:React Native 渲染器的状态更新

C++ 组件可以拥有状态,如果C++状态改变了怎么渲染呢,这种场景很少,我们在此不做深入了解,想了解的话看链接。
https://www.react-native.cn/docs/render-pipeline

从入口代码开始分析

入口文件:

import { AppRegistry } from 'react-native';
AppRegistry.registerComponent('wiseDerma', () => App);

这样,js 就在 app 端注册好了,接着就等待Native事件驱动渲染JS端定义的APP组件。

通信机制

JS调用java

java部分:
只需要在函数上加上ReactMethod注解就可以了

js部分:
JS调用java 使用通过扩展模块require(‘NativeModules’)获取native模块,然后直接调用native公开的方法

var { NativeModules } = require('react-native');
NativeModules.initialize

JS 调用require(‘NativeModules’)实际上是获取MessageQueue里面的一个native模块列表的属性。

java调用JS

Native端调用Web端,这个比较简单,JavaScript作为解释性语言,最大的一个特性就是可以随时随地地通过解释器执行一段JS代码,所以可以将拼接的JavaScript代码字符串,传入JS解析器执行就可以,JS解析器在这里就是webView。

mWebView.evaluateJavascript("javascript: 方法名('参数,需要转为字符串')", new ValueCallback() {@Overridepublic void onReceiveValue(String value) {//这里的value即为对应JS方法的返回值}
});

参考资料

https://cloud.tencent.com/developer/article/1751479
https://cloud.tencent.com/developer/article/1036325
https://zhuanlan.zhihu.com/p/281238593
https://mp.weixin.qq.com/s?__biz=MzI1NTg3NzcwNQ==&mid=2247485511&idx=1&sn=a0d97607924513c9d794e8d4fe21072f&utm_source=tuicool&utm_medium=referral
https://mp.weixin.qq.com/s/6q5r9CMmIL3O5yyHRTU5FA
简书-2022 年 React Native 的全新架构更新
yoga
官方文档架构介绍
React Native 渲染原理

RN:React Native原理以及新架构JSI、Fabric等概念相关推荐

  1. [RN] React Native 键盘管理 在Android TextInput遮盖,上移等问题解决办法

    [RN] React Native 键盘管理 在Android TextInput遮盖,上移等问题解决办法 参考文章: (1)[RN] React Native 键盘管理 在Android TextI ...

  2. [RN] React Native 实现图片预览

    [RN] React Native 实现图片预览 效果预览: 代码如下: 'use strict'; import React, {Component} from 'react'; import {I ...

  3. RN新架构 JSI 介绍

    JSI是RN新架构实现JS与Native通信的基石,Turbomodules 也是基于 JSI 实现的. 对于了解RN新架构来说,先搞明白 JSI 是至关重要的,那下面就让我们来聊一聊 JSI. 一. ...

  4. RN(React Native)

    RN是React的原生组件库 网址:React Native 中文网 · 使用React来编写原生应用的框架 一.ReactNative开发环境的搭建 在本机搭建RN项目开发运行环境 -- 3GB+ ...

  5. 【500强外资金融科技中心】Seeking研发(Java/React Native)/测试/DevOps/项目管理/架构师

    关于深圳保诚科技 深圳保诚科技有限公司 深圳保诚科技是英国保诚集团亚洲金融科技中心. 英国保诚集团(简称"保诚集团/Prudential")于1848年在伦敦成立,是一家国际金融集 ...

  6. [RN] React Native 实现 类似京东 的 沉浸式状态栏和搜索栏

    React Native 实现 类似京东 的 沉浸式状态栏和搜索栏 其原理其实就是在要 隐藏 部分的那个View 前面加入 StatusBar 代码! 代码如下: <StatusBaranima ...

  7. Taro3.2 适配 React Native 之运行时架构详解

    导读 由 58 前端团队主导的 Taro 3 适配 React Native 工作已完成有一段时间了.目前发布了多个体验版,也将在3月底迎来正式版.基于 Taro 的良好架构演变,适配 React N ...

  8. (笔记) RN React Native 热更新(react-native-code-push) AppCenter

    苹果App允许使用热更新Apple's developer agreement, 为了不影响用户体验,规定必须使用静默更新. Google Play不能使用静默更新,必须弹框告知用户App有更新.中国 ...

  9. [RN] React Native 自定义 底部 弹出 选择框 实现

    React Native 自定义 底部选择框 实现 效果如图所示: 实现方法: 一.组件封装 CustomAlertDialog.js import React, {Component} from ' ...

最新文章

  1. 怎么计算z=x+y的概率密度_amp;@*!#+用西语到底该怎么说?
  2. 数据结构——非线性结构
  3. L3-021 神坛(极角排序求三角形最小面积)
  4. 深入理解PHP Opcode缓存原理
  5. C++ 用new 动态创建多维数组
  6. Python+pandas+matplotlib控制不同曲线的属性
  7. 基于8086CPU微处理器的汇编学习之内存空间的编辑
  8. 为什么越来越多的人喜欢旅游?
  9. [学习笔记]斯坦纳树
  10. 寻找最小term,自制面试题
  11. 数字后端概念——padding
  12. mysql workbench6.3.5_mysql workbench
  13. 电热玻璃水壶CE认证检测标准介绍
  14. 欧拉角、万向节死锁理解
  15. Jetson Nano部署YOLOv5与Tensorrtx加速——(自己走一遍全过程记录)
  16. iOS App开发那些事:如何选择合适的人、规范和框架?
  17. 据说可以坚持练习可以长高
  18. 盘古越狱工具 v1.1 官方免费版
  19. python 各种模块学习
  20. 2022年最新微信小程序支付后端代码通俗易懂一篇文章让你了解微信支付

热门文章

  1. 15米 GMSLFPDLink传输线束降速问题
  2. Subversive or Subclipse
  3. Android开发最近新闻和功能都在这里了
  4. Ubuntu、stm32下的C程序各变量的分配地址分析
  5. 基于人工智能的软件测试
  6. linux slub分配器,Vi Linux内存 之 Slub分配器(六)
  7. 【许晓笛】EOS 什么是智能合约(3)
  8. 手机APP(功能)测试重点
  9. 6 数据库设计:实体-联系方法
  10. 记录达梦一次IO问题的分析