跳到主要内容

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

· 阅读需 15 分钟
Gabriel Donadel Dall'Agnol
Gabriel Donadel Dall'Agnol
Software Engineer @ Expo
Siddharth Kulkarni
Siddharth Kulkarni
Software Engineer @ Coinbase
Thibault Malbranche
Thibault Malbranche
Lead Mobile Engineer @ Brigad
Blake Friedman
Blake Friedman
Software Engineer @ Meta
Riccardo Cipolleschi
Riccardo Cipolleschi
Software Engineer @ Meta
Nicola Corti
Nicola Corti
Software Engineer @ 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 官方的发布博客中了解更多新特性。

一个显著且备受期待的功能是对 % 值的支持,适用于多处属性,比如 gapstranslation

信息

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

gap 中的百分比值支持

在 0.75 版本中,gapcolumnGaprowGap 属性(详见此处)现在支持传入带 % 的字符串值。

例如:

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以来,我们发布了大量 bug 修复及稳定性改进。

我们的目标是使新架构能尽快被视为稳定版本。因此,近几个月我们专注于弥合新旧架构之间的差距。以下是部分已解决的 Bug 和缺失功能示例:

  • 修复 Android 上 adjustsFontSizeToFit 的问题 (#44075)
  • 修复 Android 上内联视图中 textAlign 无效的问题 (#44146)
  • 修复 iOS 上文字基线被上移的问题 (#44932)

联合 Expo 团队,我们还致力于在React Native Directory中添加新架构支持的信息,让您一眼就能分辨出库是否已经支持新架构:

React Native Directory

我们邀请您参与新架构问卷调查,该调查对我们收集宝贵反馈、规划新架构推广的下一步至关重要。

此外,我们还发布了关于新架构中支持 UIManager的文章,里面概述了 Android 上 UIManager API 以及该 API 如何助力更高级应用和库的迁移。

本次发布还包含一个新的、现已官方推荐访问 jsi::Runtime 的 API。

在 TurboModules 中访问 jsi::Runtime

过去,原生模块没有官方推荐的方式访问 jsi::Runtime,使用者通常采用绕过框架的危险方法。在 0.74 版本中,我们引入了实验性 API 以安全访问 jsi::Runtime,0.75 版本我们很高兴宣布这些 API 已稳定。

访问 jsi::Runtime 的示例

在 iOS 上,可以让您的 Turbo 原生模块遵循 RCTTurboModuleWithJSIBindings 协议。您可以实现 installJSIBindingsWithRuntime,从而线程安全地访问 runtime。

@interface RCTSampleTurboModule () <RCTTurboModuleWithJSIBindings>
@end

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

在 Android 上,可以让您的 Turbo 原生模块实现 TurboModuleWithBindings 接口。您可以实现 JNI 方法 getBindingsInstaller,从而以线程安全的方式访问 C++ 端的 runtime。

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 线程且需要访问 runtime,我们引入了新的 CallInvoker API。它有一个方法 invokeAsync,将在 JS 线程安全地执行与 JS runtime 相关的工作。此 API 向前兼容。

iOS 上已提供协议 RCTCallInvokerModule。遵循该协议后,运行时会为模块装饰访问 CallInvoker 的功能。

@interface RCTSampleTurboModule() <RCTCallInvokerModule>

[self.callInvoker invokeAsync:^(jsi::Runtime& runtime) {
// 在 JS 线程执行操作
}];

在 Android 上,CallInvoker 可通过 JNI 的 CallInvokerHolder 包装,从 ReactContext 获取后,您可以调用 invokeAsync

// Java
public abstract CallInvokerHolder getJSCallInvokerHolder();
// C++
jsCallInvokerHolder->cthis()->getCallInvoker()->invokeAsync([&](jsi::Runtime& rt) {
// 在 JS 线程执行操作
});

使用框架

正如我们在今年早些时候的 React Conf 上分享的,推荐使用框架(如 Expo)来构建 React Native 应用。

您可以阅读此前博客文章《通过框架构建 React Native 应用》了解更多。

我们希望为新用户打造成功的起点。我们认为使用框架能最大化您的生产力,并在构建新应用时带来最佳开发体验。

为反映这一推荐,本版本包括以下变更:

  • 我们将 /template 文件夹从 react-native 包中移出,迁移到独立仓库:react-native-community/template
  • 我们计划在 2024 年 12 月 31 日终止 react-native init 命令的支持。

如果您已经使用如 Expo 这样的框架,这些变更不会对您造成影响。您可以在 Expo SDK 51 中使用 React Native 0.75(具体操作见Expo 专门发布的教程)。

如果您未使用框架或自行构建框架,以下是这些变更对您的影响。

模板迁移到 react-native-community/template

此前,react-native NPM 包内附带 /template 文件夹,Community CLI 用它来创建新项目。这样导致模板更新缓慢,因为每次模板改动都需发布新的 React Native 版本。

鉴于我们最新的框架使用推荐,内置一个固定风格的模板与我们的愿景不符。

因此,我们将模板迁移至 @react-native-community/template 包。

这让社区更容易维护和发展模板,无需每次变更都依赖 React Native 的发布。另外,这将模板更靠近 Community CLI,让大家更方便地检查和依赖该包。

对于使用 Community CLI 创建新项目的用户而言,此变更完全透明。未来相关模板问题请提交至模板问题仓库。诸如 upgrade-helper 等依赖该模板的工具也已相应更新,继续正常工作。

终止 react-native init

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

过去,react-native init 是创建新 React Native 项目的默认命令。但在 2024 年,我们认为该命令无法提供框架带来的同等体验。因此,我们不再推荐使用它,而是建议通过框架如 Expo 创建项目。

您仍可用 Community CLI 与模板通过 react-native init 创建项目,但会看到如下警告:

Init Deprecation

自 2024 年 12 月 31 日起,init 命令将无法创建项目。届时您需选择:

  • 使用如 Expo 的框架,使用其专用命令创建新项目,如 npx create-expo-app
  • 直接调用 Community CLI,命令为 npx @react-native-community/cli init

请注意,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 中的 Touchable 不再能在泛型表达式中作为类型使用

TouchableOpacityTouchableHighlight 组件已转为函数组件,不能再同时作为“值”与“类型”使用。例如以下代码已无效:

import {TouchableHighlight} from 'react-native';
const ref = useRef<TouchableHighlight>();
// ^^^ TS2749: TouchableHighlight 指的是一个值,不能用作类型。
// 是否意图使用 typeof TouchableHighlight?

应替换为使用内置 React 类型 React.ElementRef,或采用 View 类型:

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

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

这并非严格的 0.75 破坏性变更,但需说明:React Native 0.75 是最后一个支持 minSdk 23(Android 6.0)和 minIOSVersion 13.4 的版本。

从 0.76 版本起,minSdk 将升级至 24(Android 7.0),minIOSVersion 将升级至 15.1。

详细信息分别在我们的官方声明中可查看 AndroidiOS

Android:删除了 JSIModule

com.facebook.react.bridge.JSIModule (源码) 是我们曾为临时允许原生模块直接访问 Android 上 JSI 引入的 API。

此 API 在 0.74 中已被弃用,我们发现开源社区无意义使用,因此在 0.75 中删除。

建议改用 Turbo Native Modules 替代方案。

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 引入,用于直接从内存加载 bundle 运行。但该功能现已被默认的 JS 引擎 Hermes 替代,请勿继续使用该命令。

profile-hermes 命令是用于对 JS 代码 CPU 性能进行分析的工具,使用旧版 .cpuprofile 格式,已不被新版 Chrome 支持。作为独立命令的该功能也将退出,我们将重点提升调试工具的质量。CPU 分析现可在 实验性新调试器 的“Profiler”面板访问(注:使用 Hermes 时无法通过 Chrome 访问)。

其他破坏性变更

通用

  • Codegen
    • 生成的纯 C++ TurboModules 类和结构体名略作修改,移除 Cxx 标志
    • 不再支持浮点枚举,以避免精度误差
    • 当 JS 传入 null 给原生不可空参数时抛出错误
  • Lint
    • ESLint 配置不再在 lint 时运行 prettier
  • C++
    • ScrollViewShadowNode 构造函数需新增 bool includeTransform 参数
    • 移除 RuntimeExecutor 的 executeAsynchronouslyexecuteSynchronously_CAN_DEADLOCK
    • JsErrorHandlingFunc 改名为 OnJsError,见 JsErrorHandler.h
    • handleJsError 改名为 OnJsError,见 handleFatalError.h
    • ReactPrimitives.h 移除未使用的 import
    • LongLivedObjectCollectionLongLivedObject 的 get 方法现须传入 Runtime 参数
    • utils/jsi.h 重命名为 jsi-utils.h
  • TextInput
    • 移除已废弃的 onTextInput 回调
  • Pressability
    • 移除方法 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
  • Runtime
    • 移除 ReactHostImpl 中的 ReactJsExceptionHandler
    • 不使用默认模板时,应用需自行提供核心 turbomodules
  • DevSupport
    • 修改 DevSupportManagerFactory.create(),新增 PausedInDebuggerOverlayManager 参数
  • 测量
    • 删除 UIManagerModule.measureLayoutRelativeToParent()

iOS

  • Runtime
    • 移除 [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
  • TextInput
    • 移除已废弃的 onTextInput 回调

致谢

React Native 0.75 包含 1491+ 个提交,由 165 位贡献者 完成。感谢大家的辛勤付出!

感谢以下作者协助撰写本次发布文档的功能介绍:

升级到 0.75

请使用 React Native Upgrade Helper 查看不同版本间的代码变更,同时参考官方升级文档。

创建新项目使用命令:

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

如果您使用 Expo,React Native 0.75 已被支持在 Expo SDK 51 中(如何在 Expo 项目中升级请见此专门教程)。

信息

0.75 现为 React Native 最新稳定版本,0.72.x 进入非支持状态。详情请见 React Native 支持政策。我们计划不久后发布 0.72 的最终生命终止更新。