跳到主要内容

React Native 0.75 - 布局支持百分比值、新架构稳定化、模板与初始化更新等

·阅读约 16 分钟
Gabriel Donadel Dall'Agnol
Gabriel Donadel Dall'Agnol
Expo 软件工程师
Siddharth Kulkarni
Siddharth Kulkarni
Coinbase 软件工程师
Thibault Malbranche
Thibault Malbranche
Brigad 移动工程主管
Blake Friedman
Blake Friedman
Meta 软件工程师
Riccardo Cipolleschi
Riccardo Cipolleschi
Meta 软件工程师
Nicola Corti
Nicola Corti
Meta 软件工程师

今天我们很高兴发布 React Native 0.75!

此版本包含多项功能,例如支持 `%` 值的 Yoga 3.1、新架构的多项稳定性修复,以及引入推荐用户使用 React Native 框架的建议。

亮点

重大变更

亮点

Yoga 3.1 和布局改进

自我们在 React Native 0.74 中发布 Yoga 3.0 版本以来,我们一直在为您的应用程序推出许多改进和新的布局功能。React Native 0.75 附带 Yoga 3.1,您可以在 Yoga 官方发布博客文章中了解更多新内容。

一个值得注意且备受期待的功能是支持在各种地方使用 `%` 值,例如 `gaps` 和 `translation`

信息

这些功能仅适用于新架构。如果您热衷于使用它们,请考虑迁移到新架构。

Gaps 中的百分比值

在 0.75 版本中,此处描述的 `gap`、`columnGap` 和 `rowGap` 属性现在支持带有 `%` 值的字符串。

例如

function App(): React.JSX.Element {
return (
<SafeAreaView
style={{
marginTop: 20,
alignItems: 'center',
flex: 1,
rowGap: '20%',
}}>
<View
style={{flex: 1, flexDirection: 'row', columnGap: '10%'}}>
<View
style={{
backgroundColor: 'purple',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'blue',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'green',
width: 100,
height: 100,
}}
/>
</View>
<View
style={{flex: 1, flexDirection: 'row', columnGap: '10%'}}>
<View
style={{
backgroundColor: 'lime',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'yellow',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'orange',
width: 100,
height: 100,
}}
/>
</View>
<View
style={{flex: 1, flexDirection: 'row', columnGap: '10%'}}>
<View
style={{
backgroundColor: 'red',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'violet',
width: 100,
height: 100,
}}
/>
<View
style={{
backgroundColor: 'magenta',
width: 100,
height: 100,
}}
/>
</View>
</SafeAreaView>
);
}

将渲染如下

AndroidiOS
Android GapsiOS Gaps

Translation 中的百分比值

transform 属性现在也可以接受 % 作为 `translate` 变换的值。

例如,以下组件会将红色方块的 X 坐标移动其宽度的 100%,Y 坐标移动其高度的 100%

function Translated() {
return (
<SafeAreaView
style={{
marginTop: 20,
flex: 1,
rowGap: '20%',
}}>
<View
style={{
backgroundColor: 'red',
width: 100,
height: 100,
transform: [{translateY: '100%'}, {translateX: '100%'}],
}}
/>
</SafeAreaView>
);
}

将渲染如下

AndroidiOS
Android TranslationiOS Translation

新架构稳定化

自我们在 React Conf 上宣布新架构处于 Beta 阶段以来,我们发布了多项错误修复和稳定性改进。

我们的目标是让新架构在不久的将来被认为是稳定的。因此,在过去几个月里,我们专注于弥合旧架构和新架构之间的差距。我们修复的一些错误和缺失功能示例如下:

  • 修复 Android 上 adjustsFontSizeToFit 的问题 (#44075)
  • 修复 Android 上 textAlign 在内联视图中不起作用的问题 (#44146)
  • 修复 iOS 上文本基线向上移动的问题 (#44932)

我们还与 Expo 的同事们合作,在 React Native Directory 中添加了有关新架构支持的信息,这样您就可以立即清楚地知道一个库是否已经支持新架构

React Native Directory

我们还邀请您参与新架构调查。这项调查对我们收集关于新架构推广下一步的宝贵反馈至关重要。

我们还想分享一篇在新架构工作组中发布的关于在新架构中支持 UIManager 的文章。这篇文章提供了 Android 上 UIManager API 的概览,以及它如何帮助更高级的应用和库进行迁移。

此版本还包含一个新的 API,它现在是访问 jsi::Runtime 的推荐方式。

在 TurboModules 中访问 jsi::Runtime

过去,原生模块从未有过推荐的访问 jsi::Runtime 的方式,消费者会以危险的方式绕过框架来这样做。在 0.74 版本中,我们引入了实验性 API,提供对 jsi::Runtime 的安全访问,我们很高兴宣布它们在 0.75 中的稳定性。

如何访问 jsi::Runtime 的示例

在 iOS 上,您可以让您的 Turbo 原生模块符合 RCTTurboModuleWithJSIBindings 协议。您现在可以实现 installJSIBindingsWithRuntime,这将为您提供线程安全的运行时访问。

@interface RCTSampleTurbo Module () <RCTTurboModuleWithJSIBindings>
@end

#pragma mark - RCTTurboModuleWithJSIBindings
- (void)installJSIBindingsWithRuntime:(jsi::Runtime &)runtime {
runtime.global().setProperty(
runtime,
"myGlobalFunction",
jsi:: Function::createFromHost Function(...));
}

在 Android 上,您可以让您的 Turbo 原生模块符合 TurboModuleWithBindings 接口。您现在可以实现 JNI 方法 getBindingsInstaller,这将为您提供在 C++ 中线程安全的运行时访问。

public class SampleTurboModule extends NativeSampleTurboModuleSpec implements TurboModuleWithJSIBindings

@Override
public native BindingsInstallerHolder getBindingsInstaller();
// C++
jni::local_ref<BindingsInstallerHolder::javaobject> SampleTurboModuleJSIBindings::getBindingsInstaller(jni::alias_ref<jni::object> jobj) {
return BindingsInstallerHolder::newObjectCxxArgs(
[](jsi::Runtime& runtime) {
runtime.global().setProperty(
runtime,
“myGlobalFunction”,
jsi::Function::createFromHostFunction(...));
}
);
}

如果您在 UI 线程上需要访问运行时,我们引入了一个新的 API:CallInvoker。它由一个方法 invokeAsync 组成,该方法将跳转到 JS 线程,以使用 JS 运行时安全地执行一些工作。这些 API 向前兼容。

在 iOS 上,我们提供了 RCTCallInvokerModule 协议。在符合此协议后,我们的基础设施将通过访问 CallInvoker 来装饰该模块。

@interface RCTSampleTurboModule() <RCTCallInvokerModule>

[self.callInvoker callInvoker].invokeAsync([&](jsi::Runtime& runtime) {
// do stuff on JS thread
}

在 Android 上,可以通过 ReactContext 中的一个名为 CallInvokerHolder 的 JNI 包装器访问 CallInvoker,您可以在跨越 JNI 边界后调用 invokeAsync

// Java
public abstract CallInvokerHolder getJSCallInvokerHolder();
// C++
jsCallInvokerHolder->cthis()->getCallInvoker()->invokeAsync([&](jsi::Runtime& rt) {
// do stuff on JS thread
});

使用框架

正如我们今年早些时候在 React Conf 上分享的那样,现在构建 React Native 应用的推荐方式是通过框架,例如 Expo。

您可以在我们之前的博客文章中阅读更多关于此指南的信息:"使用框架构建 React Native 应用"。

我们希望帮助新的 React Native 用户取得成功。我们相信使用框架可以让您尽可能提高生产力,并在构建新应用时提供最佳的开发者体验。

为了反映这些建议,此版本包含以下更改

  • 我们将 /template 文件夹从 react-native 包移动到一个单独的仓库:react-native-community/template
  • 我们将在 2024 年 12 月 31 日停止支持 react-native init 命令。

如果您已经在使用像 Expo 这样的框架,这些更改将完全不影响您。您将能够将 React Native 0.75 与 Expo SDK 51 一起使用(您可以在这篇专门的 Expo 文章中找到操作说明)。

如果您没有使用框架或正在构建自己的框架,让我们看看这些更改将如何影响您。

将模板移动到 react-native-community/template

从历史上看,react-native 通常在 NPM 包中附带一个 /template 文件夹,社区 CLI 使用它来创建新项目。这使得模板更新非常缓慢,因为每次模板更改都需要发布新的 React Native 版本。

随着我们最近推荐使用框架,我们认为在核心 NPM 包中提供一个带有强烈倾向的模板与我们的愿景不符。

因此,我们决定将模板移动到 @react-native-community/template 包。

这将使社区更容易维护和发展模板,而无需每次更改都依赖 React Native 的发布。此外,这使得模板更接近社区 CLI,并将使每个人更容易将模板作为独立包进行检查和依赖。

对于使用社区 CLI 创建新项目的用户来说,此更改应该是完全透明的。从现在开始,与模板相关的新问题应在模板问题追踪器上报告。所有依赖于模板的各种工具,如升级助手,也已相应更新,并将继续正常工作。

停止支持 react-native init

与模板类似,react-native init 命令也进行了调整,以符合新的建议。

历史上,react-native init 是创建新 React Native 项目的默认命令。然而,在 2024 年,我们认为此命令无法提供框架所能提供的相同入门体验。这就是我们不再推荐它的原因,相反,您应该使用像 Expo 这样的框架。

您今天仍然可以使用 react-native init 通过社区 CLI 和模板创建新项目,但您会看到以下警告

Init Deprecation

从 2024 年 12 月 31 日起,init 命令将不再创建项目。您将不得不选择以下方式之一:

  • 使用像 Expo 这样的框架,它有自己专门的命令来创建新项目,例如 npx create-expo-app
  • 直接使用 npx @react-native-community/cli init 调用社区 CLI

请注意,react-native config 和除 init 之外的所有其他命令将继续正常工作。

信息

为了提供更平滑的迁移体验,react-native@0.75.0 包仍依赖于 @react-native-community/cli,但我们计划在不久的将来移除此依赖。

自动链接性能改进

在更新 init 命令的这项工作中,我们还花时间重写了自动链接逻辑,使其性能更高。这使得 Android 和 iOS 的构建速度都更快。

在 React Native 0.75 中,如果您正在使用 Expo,自动链接步骤现在在 Android 上可以快约 6.5 倍,在 iOS 上可以快约 1.5 倍。您可以在此处阅读更多关于这些改进的信息。

重大变更

虽然即将到来的这一部分内容看起来很长,但我们预计这里的重大变更主要会影响一小部分以更高级方式使用 React Native 的用户。

我们在此介绍它们,以求完整性和作为参考。

TypeScript 中的 Touchables 不能再用作泛型表达式中的类型

TouchablesOpacityTouchableHighlights 组件已转换为函数式组件。这意味着它们不能再用作 value & type。因此,例如,以下代码不再有效:

import {TouchableHighlight} from 'react-native';
const ref = useRef<TouchableHighlight>();
// ^^^ TS2749: TouchableHighlight refers to a value, but is being used as a type here.
// Did you mean typeof TouchableHighlight?

相反,您应该使用内置的 React 类型 React.ElementRef,或者,使用 View 类型

import {TouchableHighlight} from 'react-native';
const ref1 =
useRef<React.ElementRef<typeof TouchableHighlight>>();
// or
const ref2 = useRef<View>();

支持 minSdk 23 和 minIOSVersion 13.4 的最后一个版本

这些本身并非 0.75 中的重大变更,但我们想分享的是,React Native 0.75 将是 React Native 支持 minSdk 23 (Android 6.0) 和 minIOSVersion 13.4 的最后一个版本。

从 React Native 0.76 开始,minSdk 版本将是 24 (Android 7.0),minIOSVersion 将是 15.1。

您可以在我们针对AndroidiOS的官方公告中阅读更多相关信息。

Android:JSIModule 已删除

com.facebook.react.bridge.JSIModule (源代码) 是我们临时引入的一个 API,允许原生模块直接在 Android 上访问 JSI。此 API 的访问器在 0.74 中已弃用,我们验证了在开源中没有有意义地使用此 API,因此我们将在 0.75 中将其移除。作为替代方案,您可以使用Turbo 原生模块

Android:弹出菜单已移动到单独的包

在 0.74 中,我们将 Android 的 PopUpMenu 移动到了 @react-native 范围下的一个单独包中。在 0.75 中,我们正在移除核心库中剩余的方法:

  • UIManagerModule.showPopupMenu()
  • UIManagerModule.dismissPopupMenu()

作为替代方案,请使用 <PopupMenuAndroid /> 组件,它位于 @react-native/popup-menu-android 包中。

iOS:PushNotificationIOS 弃用工作已完成

在 0.74 中,我们弃用PushNotificationIOS 模块中的一些 API。

在 0.75 中,我们删除了这些 API,以摆脱旧的通知元数据表示形式。

已删除的 API 包括

  + (void)didReceiveLocalNotification:(UILocalNotification *)notification;
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification;

相反,请使用 didReceiveNotification:(UNNotification *)notification

社区 CLI:移除 ram-bundle 和 profile-hermes 命令

我们在此宣布社区 CLI 中的两项重要移除:命令 ram-bundleprofile-hermes

ram-bundle 命令在 React Native 0.59 中引入,允许您通过直接在内存中加载捆绑包来运行它们。此功能现在已被我们的默认 JS 引擎 Hermes 取代。您不应再使用 ram-bundle 命令。

profile-hermes 命令是一个帮助您分析 JavaScript 代码 CPU 性能的工具。它使用旧的 .cpuprofile 格式,此格式在最新版本的 Chrome 中不再受支持。将此功能作为独立命令也正在被我们放弃,因为我们致力于提高调试工具的质量标准。现在可以通过实验性新调试器中的“Profiler”面板访问 CPU 分析(注意:如果从 Chrome 连接到 Hermes,则无法访问此功能)。

其他重大变更

一般

  • 代码生成
    • 轻微更改了纯 C++ TurboModules 生成的类和结构体的名称。我们从它们的名称中去掉了 Cxx 标记
    • 由于可能存在的精度误差,浮点枚举不再受支持
    • 当在 JS 中将 null 传递给原生代码中不可为空的参数时抛出错误
  • 代码检查
    • ESLint 配置在代码检查时不再运行 Prettier
  • C++
    • ScrollViewShadowNode 现在在构造函数中需要一个新的 bool includeTransform 参数
    • 从 RuntimeExecutor 中移除了 executeAsynchronouslyexecuteSynchronously_CAN_DEADLOCK
    • JsErrorHandler.h 中将 JsErrorHandlingFunc 重命名为 OnJsError
    • handleFatalError.h 中将 handleJsError 重命名为 OnJsError
    • ReactPrimitives.h 中移除了未使用的 import
    • LongLivedObjectCollectionLongLivedObject 的 get 方法现在接受一个 Runtime 参数
    • utils/jsi.h 文件重命名为 jsi-utils.h
  • 文本输入
    • 移除了已弃用的 onTextInput 回调
  • 可按压性
    • 移除了 onLongPressShouldCancelPress_DEPRECATEDonResponderTerminationRequest_DEPRECATEDonStartShouldSetResponder_DEPRECATED 方法

Android

  • ReactViewBackgroundDrawable
    • 已弃用,转而使用 CSSBackgroundDrawable。这也从 ReactViewBackgroundDrawableColorUtil 中移除了一些 API
  • ReactContext
    • ReactContextReactApplicationContext 现在是抽象类。请改用 BridgeReactContextBridgelessReactContext
    • 删除 ReactContext.initializeWithInstance()。请改用 BridgeReactInstance
    • 移除 BridgelessReactContext.getJavaScriptContextHolder()。请改用 BridgelessCatalystInstance
    • 移除 ReactContext.getRuntimeExecutor()。请改用 BridgelessCatalystInstance
  • 布局
    • 支持百分比 flex gap 值。这会将 setGapsetRowGapsetColumnGap 等一些方法的参数从 float 更改为 dynamic
    • Android Manifest 中需要 supportsRTL
  • 运行时
    • 从 ReactHostImpl 中移除了 ReactJsExceptionHandler
    • 当不使用默认模板时,应用程序负责返回核心 turbomodule
  • 开发支持
    • DevSupportManagerFactory.create() 更改为接受新的 PausedInDebuggerOverlayManager 参数
  • 测量
    • 删除了 UIManagerModule.measureLayoutRelativeToParent()

iOS

  • 运行时
    • 移除 [RCTHost getSurfacePresenter][RCTHost getModuleRegistry]
    • 移除 EventPriority 类,并始终使用默认的 EventPriority::AsynchronousBatched。如果构建失败,请移除所有对 EventPriority 的使用
  • 图像
    • 移除未使用的 RCTImageLoadingPerfInstrumentationEnabled
  • 错误处理
    • 移除通过 RCTBridge 访问 RCTRedBox 的方式
  • CocoaPods
    • BUILD_FROM_SOURCE 重命名为 RCT_BUILD_HERMES_FROM_SOURCE
    • React-Codegen 重命名为 ReactCodegen,以更好地兼容 use_frameworks 和 Swift
  • 文本输入
    • 移除已弃用的 onTextInput 回调

致谢

React Native 0.75 包含了来自 165 位贡献者的超过 1491 次提交。感谢您的辛勤工作!

感谢所有参与撰写此发布文章中功能文档的额外作者

升级到 0.75

请使用 React Native 升级助手来查看现有项目在 React Native 版本之间的代码变更,以及升级文档。

创建新项目

npx @react-native-community/cli@latest init MyProject --version latest

如果您使用 Expo,Expo SDK 51 将支持 React Native 0.75(有关如何在您的 Expo 项目中将 React Native 更新到 0.75.0 的说明,请参阅这篇专门的文章)。

信息

0.75 现在是 React Native 的最新稳定版本,0.72.x 不再受支持。更多信息请参见 React Native 的支持策略。我们计划在不久的将来发布 0.72 的最终生命周期结束更新。