跳到主要内容

关于新架构

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

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

React Native 0.68 版本起,新架构已可选择性启用(实验性),并在后续每个版本中持续改进。团队目前正致力于使其成为 React Native 开源生态系统的默认体验。

为什么要采用新架构?

在多年的 React Native 构建实践后,团队发现了一系列限制,这些限制阻碍了开发者打造某些高度精致的体验。这些限制是框架现有设计的根本问题,因此新架构最初是对 React Native 未来的一项投资。

新架构带来了旧有架构无法实现的功能和改进。

同步布局和效果

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

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

通过新架构,我们可以完全避免这个问题,因为它提供了对布局信息的同步访问和正确调度的更新,从而确保用户看不到任何中间状态。

示例:渲染 Tooltip

测量并将提示(tooltip)放置在视图上方,可以展示同步渲染所带来的可能性。提示需要知道其目标视图的位置,以确定它应该在哪里渲染。

在现有架构中,我们使用 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、Transitions 等新 React API,进一步统一 Web 和原生 React 开发的代码库和概念。

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

示例:自动批处理

通过新架构,你将获得 React 18 渲染器的自动批处理功能。

在此示例中,一个滑块指定要渲染的平铺(tiles)数量。将滑块从 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 如何中断正在进行的渲染以处理更新的状态更新。

我们将平铺(tile)数量的状态更新包装在 startTransition 中,以表明平铺的渲染可以被中断。startTransition 还提供了一个 isPending 标志,用于告诉我们过渡何时完成。

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

你会注意到,在 Transitions 中频繁更新时,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.
未将其标记为过渡渲染平铺。查看代码

快速 JavaScript/原生接口

新架构移除了 JavaScript 和原生代码之间的异步桥接,并将其替换为 JavaScript Interface (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
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