React-native的新架构
本文总结:
文章主要介绍了 React Native 的新架构,包括以下几个方面的内容:📱✨
-
如何抹平 iOS 和 Android 样式差异,提升跨平台一致性;
-
分析了旧架构中存在的问题,如通信瓶颈、启动慢、维护复杂等;
-
介绍了 JSI 中间层 带来的变革,如:
-
不再强依赖 JavaScriptCore 引擎;
-
可直接在 JS 层调用 Native 方法,提升交互效率;
-
-
讲解了 RN 的渲染阶段流程;
-
常用基础库(如 React Navigation)的配套使用;
-
异常捕捉机制 和 热更新/包体更新的思路。
🔍 关联问题与亮点:
-
新架构优势是什么? 新架构通过引入 JSI、Fabric 和 TurboModules,解决了旧通信机制的性能瓶颈,提升了启动速度和运行效率。
-
Fabric 如何优化渲染? Fabric 渲染引擎让渲染流程更接近 React 生态,通过异步渲染、协调与 Commit 阶段的优化,实现更流畅的 UI 体验。🌈
-
Turbo Modules 怎样加速? TurboModules 通过 按需加载、延迟初始化 方式提升模块加载性能,且与 JSI 结合使 JS 层调用 Native 层更加轻量、直接。⚡
1、React-native的style
上一篇文章,我们应该已经对跨平台有了一定的概念,但这里其实有一个问题并没有解决,就是其实在ios和安卓
上的样式是有差异的,那么我们的Rn
就需要去抹平这种差异化,rn
中采用的是css-in-js
。
我们在js
中写的style
对象,将由单独的一个线程去处理,也就是Shadow thread
。在这个线程中由Yoga
引擎(这也是facebook开发的)重新去计算app
的布局,这个引擎在计算了有关app
的东西后,将结果又反馈至UI
线程,最终呈现出来。
那么一个完整的老版本的架构是这样的:
然后我们现在把整个流程理一下: 假设我们现在有一段react
的代码
<View style={{width: 200, height: 200}}/>
下一步就是js
线程将其序列化
UIManager.createView([352,"RCTView",191,{width":200,"height":200}])
而此时这个task
进入到了桥前的异步队列中,它的目的地是ShadowThread
,ShadowTread
接收到这条信息后,先反序列化,形成Shadow tree
,然后传给Yoga
,形成原生布局信息。
下一步又先序列化把信息传给native
线程,然后拿到后反序列化根据布局信息
去进行渲染和绘制。
大伙现在应该已经对一个rn
的整体架构有了基本的了解,还记得上篇文章的问题吗?桥
的负载和异步
会导致性能问题和不确定性。
-
线程信息的传递因为要减小开销每次都需要反复序列化,但序列化又是一个消耗很大的事情。
-
异步队列的不确定性,你并不能保证一个事件的顺序。
因此rn
的新架构就是要去解决这些问题,也就是现在的中间层。
2、React-native新架构
关于新架构的内容很多,可能有些地方我自己也有理解不当的地方欢迎指正。
我们先讲讲最大的改动,就rn
在新架构中直接把老的桥干掉了,直接换成了一个新的中间层或者说通用层
,也就是 JS Interface (JSI)
。在这个通用层里面有很多的新内容我们可以先看一下这个架构图。
So,我们来看看有哪些变化,上面的图中间部分,就是JSI
。(解释一下为啥这个图是这样的,因为就Turbo Modules
我其实认为是Native Moudles
的加强,而Fabric
是Renderer
的加强,他们是原本就存在的)。
1、JS-bundle
不再强依赖
于JavaScriptCore
引擎了。我们现在可以很方便用更好的引擎去替换了,性能更好了。比如Hermes
。
2、JSI
让我们可以直接在js
层调用native
的方法了。由HostObject C++ object
实现,它直接存储了native
层方法和属性的引用放在了一个全局对象上,然后我们js
就可以直接调用java/oc的api
。
3、Turbo Modules
的出现(上图中的Native Moudles),在之前的架构中 JS
使用的所有 Native Modules
(例如蓝牙、地理位置、文件存储等)都必须在应用程序打开之前进行初始化,这意味着即使用户不需要某些模块,但是它仍然必须在启动时进行初始化。
Turbo Modules
基本上是对这些旧的 Native 模块的增强,正如在前面介绍的那样,现在 JS 将能够持有这些模块的引用,所以 JS
代码可以仅在需要时才加载对应模块,这样可以将显着缩短 RN
应用的启动时间。
4、Fabric
也就是上图中的renderer
(以前shadow层是在native层实现的),一个新的UI渲染器
,它就相当于在c++中,可以直接创建一个 ShadowTree
,一个就是快,同时也减少了渲染元素的步骤。
可能大家没懂,举个例子:当 App
运行时,React
会执行你的代码并在 JS
中创建一个 ReactElementTree
,基于这棵树渲染器会在 C++
中创建一个 ReactShadowTree
。Fabric
会使用 Shadow Tree
来计算 UI
元素的位置,而一旦 Layout
完成,Shadow Tree
就会被转换为由 Native Elements
组成的 HostViewTree
(例如:RN 里的 会变成 Android 中的 ViewGroup 和 iOS 中的 UIView)。
5、 codegen
其实就是一个静态类型检查器,CodeGen
使用类型确定后的 JavaScript
来为Turbo Modules
和 Fabric
定义供他们使用的接口元素,并且它会在编译时生成更多的native
代码,而非运行时。
3、RN的渲染
将 React 代码渲染到宿主平台,我们称为渲染流水线,可大致分为三个阶段:
-
渲染(
Render
):在JavaScript
中,React
执行那些业务逻辑代码创建React
元素树(React Element Trees)。然后在C++
中,用React
元素树创建React
影子树(React Shadow Tree)。 -
提交(
Commit
):在React
影子树完全创建后,渲染器会触发一次提交。这会将React
元素树和新创建的React
影子树的提升为“下一棵要挂载的树”。 这个过程中也包括了布局信息计算。 -
挂载(
Mount
):React
影子树有了布局计算结果后,它会被转化为一个宿主视图树(Host View Tree
)。
4、一些基本的库
Ok,上面都是框架的架构设计,我们先有一个大体的概念,那么现在我们稍微走近实战去了解一些必要的包,因为后面不会怎么讲。
React-native
只内置了一些必要的包,但为了尽可能的减小包的大小,许多的包需要你自己去配置,例如:asyncStorage
,这种sdk
你需要一点点依赖相关的原生知识,但问题不大,一般都会有模版去教你,照着模版就行了(但也不一定,绝大数情况是)。那么我们现在就看看一些常用的包。
3.1、React Navigation
这个应该几乎是每个用rn
的同学都该了解的东西了,原生app
和web
的路由是不同的,在app
里其实是没有url
这种概念,在原生里要理解screen
,也就是说控制用户所见屏幕。在老版本rn
有一些原始导航组件去控制屏幕,但很复杂,所以就现在一般都会用到react-navigation
这个库。
我直接上实战吧,
import * as React from "react";import { NavigationContainer } from "@react-navigation/native";import { createNativeStackNavigator } from "@react-navigation/native-stack";import Home from "./Home";import Settings from "./Settings";const Stack = createNativeStackNavigator();export default function App() {return (<NavigationContainer><Stack.Navigator><Stack.Screen name="Home" component={Home} /><Stack.Screen name="Settings" component={Settings}/></Stack.Navigator></NavigationContainer>);}
createNativeStackNavigator是
创建你的导航组件的一个方法,它返回一个对象,里面有Screen
和Navigator
2个组件,他们用来配置导航
import React from "react";import { View, Text, Button, StatusBar } from "react-native";import styles from "./styles";
export default function Home({ navigation }) {return (<View style={styles.container}><StatusBar barStyle="dark-content" /><Text>Home Screen</Text><Buttontitle="Settings"onPress={() => navigation.navigate("Settings")}/>
</View>
);}
就看到home
组件,当你按下的时候就跳转到settings
这个屏幕上去,更多的内容我们后面实战的时候再讲吧,只是做个简单的演示。
3.2 RN组件库
antd mobile
估计国内我们基本用的都这个或者就是自己封装的组件库,推荐几个其他的NativeBase
、React Native Element
、 UI Kittern
、 React-native-paper
3.3 启动页
其实启动页就是你js
线程启动前展示的过度页面,React-native-bootsplash
。
3.4 Icon
react-native-vector-icons
、 react-native-svg
。
3.5 异常捕捉
通常,当我们开发一个web应用时,我们很好处理错误,因为它们不会超出JS
的范围。简单的说我们前端就是web
的王(掌控力),我们可以很容易地看到原因,并在DevTools
中打开日志。
但Rn
因为除了环境的JS之外,我们还有native
组件,这也可能导致app
执行中的错误。因此,当发生错误时,我们的应用程序将关闭立即我们很难弄清楚原因,因此React-native-exception-handler
也正是解决这个问题的包。
就像这样:
import { setJSExceptionHandler, setNativeExceptionHandler } from "react-native-exception-handler";
setJSExceptionHandler((error, isFatal) => {// …});const exceptionhandler = (exceptionString) => {// 处理异常代码};setNativeExceptionHandler(exceptionhandler,forceAppQuit,executeDefaultHandler);
3.6 包更新 其实如果是ios我们要更新应用上传到商店,有这么个技术OAT
可以替换js包,就可以看看微软的Codepush
4.结束
[rn中文文档地址],就这2篇文章都是在理一些基础理论的东西,对于一些组件api大伙可以看看文档。