React-native 的新架构 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
leia
V2EX    前端开发

React-native 的新架构

  •  
  •   leia 204 天前 1881 次点击
    这是一个创建于 204 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文总结:

    文章主要介绍了 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的加强,而FabricRenderer的加强,他们是原本就存在的)。

    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 ModulesFabric定义供他们使用的接口元素,并且它会在编译时生成更多的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你需要一点点依赖相关的原生知识,但问题不大,一般都会有模版去教你,照着模版就行了(但也不一定,绝大数情况是)。那么我们现在就看看一些常用的包。

    4.1 、React Navigation

    这个应该几乎是每个用rn的同学都该了解的东西了,原生appweb的路由是不同的,在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 是创建你的导航组件的一个方法,它返回一个对象,里面有ScreenNavigator2 个组件,他们用来配置导航

    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> <Button title="Settings" OnPress={() => navigation.navigate("Settings")} /> </View> ); } 

    就看到home组件,当你按下的时候就跳转到settings这个屏幕上去,更多的内容我们后面实战的时候再讲吧,只是做个简单的演示。

    4.2 RN 组件库

    antd mobile估计国内我们基本用的都这个或者就是自己封装的组件库,推荐几个其他的NativeBase、React Native ElementUI KitternReact-native-paper

    4.3 启动页

    其实启动页就是你js线程启动前展示的过度页面,React-native-bootsplash

    4.4 Icon

    react-native-vector-iconsreact-native-svg

    4.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 ); 

    4.6 包更新

    其实如果是 ios 我们要更新应用上传到商店,有这么个技术OAT可以替换 js 包,就可以看看微软的Codepush

    4.结束

    [rn 中文文档地址],就这 2 篇文章都是在理一些基础理论的东西,对于一些组件 api 大伙可以看看文档。

    欢迎加入群聊,我们一起讨论一些更有趣的技术、商业、闲聊。

    3 条回复    2025-05-29 02:13:52 +08:00
    NewYear
        1
    NewYear  
       204 天前
    你可以大胆一点。

    React Native 已经支持桌面端了,你这个内容还在围绕“抹平 iOS 和 Android 样式差异”,这就很不吸引人。
    leia
        2
    leia  
    OP
       203 天前
    @NewYear 感谢评价分享,
    leia
        3
    leia  
    OP
       203 天前
    @NewYear 确实是 这个文章是在半年前写的 感谢评价
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1208 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 17:38 PVG 01:38 LAX 09:38 JFK 12:38
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86