跳到主要内容

关于新架构

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

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

React Native 0.68 起,新架构已作为实验性选择加入项提供,并在每个后续版本中持续改进。 团队目前正在努力使之成为 React Native 开源生态系统的默认体验。

为什么需要新架构?

经过多年的 React Native 构建经验,团队发现了一系列限制,这些限制阻止开发人员使用高度润色来制作某些体验。 这些限制是框架现有设计的基础,因此新架构最初是对 React Native 未来的投资。

新架构解锁了在旧架构中不可能实现的功能和改进。

同步布局和效果

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

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

使用新架构,我们可以通过同步访问布局信息和正确计划的更新来完全避免此问题,从而使用户看不到任何中间状态。

示例:渲染工具提示

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

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

jsx
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 在单个提交中同步测量和应用布局更新,从而避免视觉“跳跃”。

jsx
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 for data-fetching、Transitions 和其他新的 React API 等功能,从而进一步统一 Web 和原生 React 开发之间的代码库和概念。

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

示例:自动批处理

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

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

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

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

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

我们将图块数量状态更新包装在 startTransition 中,以指示渲染图块可以中断。 startTransition 还提供了一个 isPending 标志,以告知我们 transition 何时完成。

jsx
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>
)
}

您会注意到,在 transition 中的频繁更新中,React 渲染的中间状态更少,因为它会在状态过时后立即退出渲染状态。 相比之下,在没有 transitions 的情况下,会渲染更多的中间状态。 这两个示例仍然使用自动批处理。 然而,transitions 为开发人员提供了更强大的功能来批处理正在进行的渲染。

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.
使用 transitions 渲染图块以中断过时状态的正在进行的渲染。 查看代码
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.
渲染图块而不将其标记为 transition。 查看代码

快速 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 项目中默认启用。

如果您发现任何运行不正常的地方,请使用此模板打开一个 issue。

如果由于任何原因您无法使用新架构,您仍然可以选择退出它

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
diff
+ 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 依赖项
shell
bundle exec pod install