跳至主要内容

关于新架构

自 2018 年以来,React Native 团队一直在重新设计 React Native 的核心内部结构,以使开发人员能够创建更高质量的体验。截至 2024 年,此版本的 React Native 已在规模上得到验证,并为 Meta 的生产应用程序提供支持。

术语“新架构”既指新的框架架构,也指将其引入开源的工作。

新架构自 React Native 0.68 起即可选择性地进行实验性使用,并在每个后续版本中持续改进。团队目前正在努力将其设为 React Native 开源生态系统的默认体验。

为什么要使用新架构?

在使用 React Native 构建多年后,团队发现了一系列限制,这些限制阻止开发人员创建某些高质量的体验。这些限制是框架现有设计的根本原因,因此新架构最初是作为对 React Native 未来的一项投资。

新架构解锁了传统架构中无法实现的功能和改进。

同步布局和效果

构建自适应 UI 体验通常需要测量视图的大小和位置并调整布局。

目前,您将使用 onLayout 事件获取视图的布局信息并进行任何调整。但是,onLayout 回调中的状态更新可能会在绘制上一个渲染后应用。这意味着用户可能会看到初始布局渲染和响应布局测量之间存在的中间状态或视觉跳跃。

借助新架构,我们可以完全避免此问题,方法是同步访问布局信息并正确安排更新,以便用户看不到任何中间状态。

示例:渲染工具提示

在视图上方测量和放置工具提示使我们能够展示同步渲染解锁的功能。工具提示需要知道其目标视图的位置才能确定其渲染位置。

在当前架构中,我们使用 onLayout 获取视图的测量结果,然后根据视图所在位置更新工具提示的位置。

function ViewWithTooltip() {
// ...

// We get the layout information and pass to ToolTip to position itself
const onLayout = React.useCallback(event => {
targetRef.current?.measureInWindow((x, y, width, height) => {
// This state update is not guaranteed to run in the same commit
// This results in a visual "jump" as the ToolTip repositions itself
setTargetRect({x, y, width, height});
});
}, []);

return (
<>
<View ref={targetRef} onLayout={onLayout}>
<Text>Some content that renders a tooltip above</Text>
</View>
<Tooltip targetRect={targetRect} />
</>
);
}

借助新架构,我们可以使用 useLayoutEffect 在单个提交中同步测量和应用布局更新,从而避免视觉“跳跃”。

function ViewWithTooltip() {
// ...

useLayoutEffect(() => {
// The measurement and state update for `targetRect` happens in a single commit
// allowing ToolTip to position itself without intermediate paints
targetRef.current?.measureInWindow((x, y, width, height) => {
setTargetRect({x, y, width, height});
});
}, [setTargetRect]);

return (
<>
<View ref={targetRef}>
<Text>Some content that renders a tooltip above</Text>
</View>
<Tooltip targetRect={targetRect} />
</>
);
}
A view that is moving to the corners of the viewport and center with a tooltip rendered either above or below it. The tooltip is rendered after a short delay after the view moves
工具提示的异步测量和渲染。 查看代码
A view that is moving to the corners of the viewport and center with a tooltip rendered either above or below it. The view and tooltip move in unison.
工具提示的同步测量和渲染。 查看代码

支持并发渲染器和功能

新架构支持并发渲染和 React 18 及更高版本中提供的功能。您现在可以在 React Native 代码中使用 Suspense 用于数据获取、过渡和其他新的 React API,从而进一步统一 Web 和原生 React 开发之间的代码库和概念。

并发渲染器还提供了开箱即用的改进,例如自动批处理,从而减少了 React 中的重新渲染。

示例:自动批处理

使用新架构,您将获得 React 18 渲染器提供的自动批处理。

在此示例中,滑块指定要渲染的图块数量。将滑块从 0 拖动到 1000 将触发一系列快速的状态更新和重新渲染。

在比较 相同代码的渲染器时,您可以直观地注意到渲染器提供了更流畅的 UI,并且中间 UI 更新更少。来自原生事件处理程序(如本机滑块组件)的状态更新现在已进行批处理。

A video demonstrating an app rendering many views according to a slider input. The slider value is adjusted from 0 to 1000 and the UI slowly catches up to rendering 1000 views.
使用传统渲染器渲染频繁的状态更新。
A video demonstrating an app rendering many views according to a slider input. The slider value is adjusted from 0 to 1000 and the UI resolves to 1000 views faster than the previous example, without as many intermediate states.
使用 React 18 渲染器渲染频繁的状态更新。

新的并发功能(如 Transitions)使您能够表达 UI 更新的优先级。将更新标记为较低优先级会告诉 React 它可以“中断”渲染更新以处理更高优先级的更新,从而确保在重要位置提供响应式用户体验。

示例:使用 startTransition

我们可以基于前面的示例来展示过渡如何中断正在进行的渲染以处理更新的状态更新。

我们将图块编号状态更新包装在 startTransition 中以指示可以中断图块渲染。startTransition 还提供了一个 isPending 标志来告诉我们过渡何时完成。

function TileSlider({value, onValueChange}) {
const [isPending, startTransition] = useTransition();

return (
<>
<View>
<Text>
Render {value} Tiles
</Text>
<ActivityIndicator animating={isPending} />
</View>
<Slider
value={1}
minimumValue={1}
maximumValue={1000}
step={1}
onValueChange={newValue => {
startTransition(() => {
onValueChange(newValue);
});
}}
/>
</>
);
}

function ManyTiles() {
const [value, setValue] = useState(1);
const tiles = generateTileViews(value);
return (
<TileSlider onValueChange={setValue} value={value} />
<View>
{tiles}
</View>
)
}

您会注意到,在过渡中进行频繁更新时,React 渲染的中间状态更少,因为它会在状态变得陈旧时立即停止渲染状态。相比之下,在没有过渡的情况下,会渲染更多中间状态。这两个示例都仍然使用自动批处理。但是,过渡为开发人员提供了更多能力来批处理正在进行的渲染。

A video demonstrating an app rendering many views (tiles) according to a slider input. The views are rendered in batches as the slider is quickly adjusted from 0 to 1000. There are less batch renders in comparison to the next video.
渲染图块并使用过渡来中断正在进行的陈旧状态渲染。 查看代码
A video demonstrating an app rendering many views (tiles) according to a slider input. The views are rendered in batches as the slider is quickly adjusted from 0 to 1000.
渲染图块但不将其标记为过渡。 查看代码

快速 JavaScript/原生接口

新架构删除了 JavaScript 和原生之间 异步桥接,并将其替换为 JavaScript 接口 (JSI)。JSI 是一个接口,允许 JavaScript 保留对 C++ 对象的引用,反之亦然。使用内存引用,您可以直接调用方法,而无需序列化成本。

JSI 使 VisionCamera(React Native 的一个流行相机库)能够实时处理帧。典型的帧缓冲区为 10 MB,具体取决于帧速率,每秒大约相当于 1 GB 的数据。与桥接的序列化成本相比,JSI 可以轻松处理大量接口数据。JSI 可以公开其他复杂的基于实例的类型,例如数据库、图像、音频样本等。

在新架构中采用 JSI 消除了所有原生-JavaScript 互操作中的此类序列化工作。这包括初始化和重新渲染本机核心组件(如 ViewText)。您可以在我们的 渲染性能调查中了解更多关于新架构的信息,以及我们测量的改进基准。

启用新架构后,我有什么可以期待的?

虽然新架构启用了这些功能和改进,但为您的应用程序或库启用新架构可能不会立即提高性能或用户体验。

例如,您的代码可能需要重构以利用新的功能,例如同步布局效果或并发功能。尽管 JSI 将最大程度地减少 JavaScript 和原生内存之间的开销,但数据序列化可能不是您应用程序性能的瓶颈。

在您的应用程序或库中启用新架构就是在选择 React Native 的未来。

团队正在积极研究和开发新架构解锁的新功能。例如,Web 对齐是 Meta 正在积极探索的一个领域,并将发布到 React Native 开源生态系统。

您可以在我们专门的 讨论和建议存储库中跟踪并做出贡献。

我今天应该使用新架构吗?

在 0.76 版本中,新架构已在所有 React Native 项目中默认启用。

如果您发现任何无法正常运行的内容,请使用 此模板提交问题。

如果出于任何原因,您无法使用新架构,您仍然可以将其禁用

Android

  1. 打开 android/gradle.properties 文件
  2. newArchEnabled 标志从 true 切换到 false
gradle.properties
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
-newArchEnabled=true
+newArchEnabled=false

iOS

  1. 打开 ios/Podfile 文件
  2. 在 Podfile 的主作用域中添加 ENV['RCT_NEW_ARCH_ENABLED'] = '0'模板中的参考 Podfile
+ ENV['RCT_NEW_ARCH_ENABLED'] = '0'
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
  1. 使用以下命令安装 CocoaPods 依赖项
bundle exec pod install