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


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

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

主要亮点

1. Yoga 3.1 和布局改进

  • 自从我们在 React Native 0.74 中发布 Yoga 3.0 以来,我们不断推出许多改进和新布局功能。React Native 0.75 随附 Yoga 3.1,您可以在 Yoga 的官方发布博客中了解更多新内容。
  • 一个显著且备受期待的功能是对 % 值的支持,可用于间隙和变换等多个地方。

提示:这些功能仅适用于新架构。如果您希望使用它们,请考虑迁移到新架构。

2. 百分比值在间隙中的应用

  • 在 0.75 版本中,gapcolumnGaprowGap 属性现在支持带 % 值的字符串。例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    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>
    );
    }
  • 将渲染如下:

Android:

iOS:

3. 百分比值在变换中的应用

  • transform 属性现在也可以接受 % 作为 translate 变换的值。例如,以下组件会将红色方块的 X 坐标移动其宽度的 100%,Y 坐标移动其高度的 100%:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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>
);
}
  • 将渲染如下:

Android:

iOS:

4. 新架构稳定化

  • 自我们在 React Conf 上宣布新架构进入 Beta 以来,我们发布了多个 Bug 修复并改善了其稳定性。
  • 我们的目标是在不久的将来将新架构视为稳定。因此,过去几个月中我们专注于弥合旧架构和新架构之间的差距。我们修复了一些 Bug 和缺失的功能,例如:
    • 修复了 Android 上的 adjustsFontSizeToFit(#44075)
    • 修复了 Android 上 textAlign 在内联视图中不起作用的问题(#44146)
    • 修复了 iOS 上文本基线被抬高的问题(#44932)
  • 与 Expo 团队合作,我们还在 React Native 目录中添加了有关新架构支持的信息,因此您可以立即了解某个库是否已经支持新架构:

  • 我们还邀请您参加新架构调查问卷。这项调查对我们至关重要,有助于我们收集有关新架构推广下一步的宝贵反馈。
  • 我们还想分享我们在新架构工作组中发布的一篇关于在新架构中支持 UIManager 的文章。本文提供了 Android 上 UIManager API 的概述,并解释了它如何帮助更高级的应用程序和库迁移。

5. 访问 jsi::Runtime 的新 API

  • 过去,没有官方推荐的方式让原生模块访问 jsi::Runtime,开发者通常会通过一些冒险的方法绕过框架。在 0.74 中,我们引入了实验性 API 提供对 jsi::Runtime 的安全访问,我们很高兴地宣布这些 API 在 0.75 中已稳定。
访问 jsi::Runtime 的示例:

在 iOS 上,您可以使您的 Turbo Native 模块符合 RCTTurboModuleWithJSIBindings 协议。您现在可以实现 installJSIBindingsWithRuntime 方法,这将使您能够线程安全地访问运行时。

1
2
3
4
5
6
7
8
9
10
@interface RCTSampleTurboModule () <RCTTurboModuleWithJSIBindings>
@end

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

在 Android 上,您可以使您的 Turbo Native 模块符合 TurboModuleWithBindings 接口。您现在可以实现 JNI 方法 getBindingsInstaller,这将使您能够在 C++ 中线程安全地访问运行时。

1
2
3
4
public class SampleTurboModule extends NativeSampleTurboModuleSpec implements TurboModuleWithJSIBindings

@Override
public native BindingsInstallerHolder getBindingsInstaller();
1
2
3
4
5
6
7
8
9
10
11
// 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 的访问。

1
2
3
4
5
@interface RCTSampleTurboModule() <RCTCallInvokerModule>

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

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

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

这个版本通过引入这些改进和功能,使 React Native 更加灵活和强大,进一步提高了开发效率。

使用框架

正如我们在今年早些时候的 React Conf 上分享的那样,构建 React Native 应用程序的推荐方式现在是通过框架,比如 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 文件夹,供社区 CLI 用来创建新项目。这使得模板更新变得相对缓慢,因为每次模板更改都需要新的 React Native 版本。

随着我们最新推荐的使用框架的方式,我们认为在核心 NPM 包内提供一个带有强烈意见的模板与我们的愿景不符。

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

这将使社区更容易维护和发展模板,而不必依赖于每次变更都发布一个新的 React Native 版本。此外,这将模板与社区 CLI 联系得更紧密,并使每个人更容易将其作为一个独立的包进行检查和依赖。

对于使用社区 CLI 创建新项目的用户来说,这一变化应该是完全透明的。从现在起,与模板相关的新问题应在模板问题跟踪器上报告。所有依赖模板的工具,例如 upgrade-helper,也已经进行了相应的更新并将继续正常工作。

停止使用 react-native init

与模板类似,为了与新的推荐方法保持一致,react-native init 命令也进行了相应的调整。

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

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

1
Init Deprecation

从 2024 年 12 月 31 日起,init 命令将不再创建项目。您将需要选择:

  • 使用框架(如 Expo),并使用其专用命令创建新项目,如 npx create-expo-app
  • 直接使用社区 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 进行更高级操作的小部分用户。

为了完整性和参考,我们在这里列出了它们。

  1. Touchable 组件在 TypeScript 中不再能用作泛型表达式中的类型

    • TouchableOpacityTouchableHighlight 组件已转换为函数组件。这意味着它们不能再用作值和类型。因此,例如,以下代码不再有效:
    1
    2
    3
    4
    import {TouchableHighlight} from 'react-native';
    const ref = useRef<TouchableHighlight>();
    // ^^^ TS2749: TouchableHighlight 指的是一个值,但在这里被用作类型。
    // 您是否是指 `typeof TouchableHighlight`?
    • 相反,您应该使用内置的 React 类型 React.ElementRefView 类型:
    1
    2
    3
    4
    5
    import {TouchableHighlight} from 'react-native';
    const ref1 =
    useRef<React.ElementRef<typeof TouchableHighlight>>();
    // 或
    const ref2 = useRef<View>();
  2. 最后支持 minSdk 23minIOSVersion 13.4 的版本

    • 这些并不是 0.75 版本本身的重大更改,但我们希望告知您,React Native 0.75 将是最后一个支持 minSdk 23(Android 6.0)和 minIOSVersion 13.4 的版本。
    • 从 React Native 0.76 开始,minSdk 版本将是 24(Android 7.0),minIOSVersion 将是 15.1。

    您可以在我们针对 Android 和 iOS 的官方公告中阅读更多内容。

  3. **删除 Android 的 JSIModule**:

    • com.facebook.react.bridge.JSIModule 是一个 API,我们临时引入了它以便原生模块能够直接在 Android 上访问 JSI。该 API 的访问器在 0.74 中已被弃用,我们验证了在开源中没有对该 API 的有意义的使用,因此我们在 0.75 中将其移除。您可以使用 Turbo Native Modules 作为替代方案。
  4. 将 Android 的 PopUp Menu 移至单独的包

    • 在 0.74 版本中,我们将 Android 的 PopUpMenu 移至 @react-native 范围下的单独包。在 0.75 版本中,我们移除了核心中仍存在的剩余方法:
    1
    2
    UIManagerModule.showPopupMenu()
    UIManagerModule.dismissPopupMenu()
    • 作为替代方案,请使用位于 @react-native/popup-menu-android 包中的 <PopupMenuAndroid /> 组件。
  5. 完成 iOS PushNotificationIOS 的弃用工作

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

    • 在 0.75 版本中,我们删除了这些 API 以迁移离开旧的通知元数据表示。

    • 删除的 API 包括:

      1
      2
      + (void)didReceiveLocalNotification:(UILocalNotification *)notification;
      + (void)didReceiveRemoteNotification:(NSDictionary *)notification;
    • 相反,使用 didReceiveNotification:(UNNotification *)notification

  6. 社区 CLI:删除 ram-bundleprofile-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,则无法访问此功能)。

      其他重大变更

通用

  1. 代码生成:

    • 略微更改了纯 C++ TurboModules 生成的类和结构的名称。我们从名称中删除了 “Cxx” 代号。
    • 由于可能的精度问题,不再支持浮点枚举。
    • 当在 JS 中传递 null 给原生代码中的非可空参数时,现在会抛出错误。
  2. 代码检查:

    • 在进行 ESLint 配置检查时,不再运行 prettier
  3. C++:

    • ScrollViewShadowNode 的构造函数现在需要一个新的布尔参数 includeTransform
    • 移除了 RuntimeExecutor 中的 executeAsynchronouslyexecuteSynchronously_CAN_DEADLOCK
    • JsErrorHandler.h 中将 JsErrorHandlingFunc 重命名为 OnJsError
    • handleFatalError.h 中将 handleJsError 重命名为 OnJsError
    • ReactPrimitives.h 中移除了未使用的导入。
    • LongLivedObjectCollectionLongLivedObjectget 方法现在接受一个 Runtime 参数。
    • utils/jsi.h 文件重命名为 jsi-utils.h
  4. TextInput:

    • 移除了已弃用的 onTextInput 回调。
  5. Pressability:

    • 移除了 onLongPressShouldCancelPress_DEPRECATEDonResponderTerminationRequest_DEPRECATEDonStartShouldSetResponder_DEPRECATED 方法。

Android

  1. ReactViewBackgroundDrawable:

    • 已弃用,改用 CSSBackgroundDrawable。这也移除了 ReactViewBackgroundDrawableColorUtil 中的一些 API。
  2. ReactContext:

    • ReactContextReactApplicationContext 现在是抽象类。请使用 BridgeReactContextBridgelessReactContext
    • 删除了 ReactContext.initializeWithInstance()。请使用 BridgeReactInstance 代替。
    • BridgelessReactContext 中移除了 getJavaScriptContextHolder()。请使用 BridgelessCatalystInstance 代替。
    • 移除了 ReactContext.getRuntimeExecutor()。请使用 BridgelessCatalystInstance
  3. 布局:

    • 支持百分比的 flex gap 值。这些更改影响了一些方法的参数,如 setGapsetRowGapsetColumnGap 的类型从 float 改为 dynamic
    • 在 Android Manifest 中要求支持 RTL(从右到左布局)。
  4. 运行时:

    • ReactHostImpl 中移除了 ReactJsExceptionHandler
    • 当不使用默认模板时,应用程序需要负责返回核心 TurboModules。
  5. 开发支持:

    • 更改了 DevSupportManagerFactory.create(),使其接受一个新的 PausedInDebuggerOverlayManager 参数。
  6. 测量:

    • 删除了 UIManagerModule.measureLayoutRelativeToParent()

iOS

  1. 运行时:

    • 移除了 [RCTHost getSurfacePresenter][RCTHost getModuleRegistry]
    • 移除了 EventPriority 类,并始终使用默认的 EventPriority::AsynchronousBatched。如果构建失败,请删除任何 EventPriority 的使用。
  2. 图像:

    • 移除了未使用的 RCTImageLoadingPerfInstrumentationEnabled
  3. 错误处理:

    • 移除了通过 RCTBridge 访问 RCTRedBox 的能力。
  4. CocoaPods:

    • BUILD_FROM_SOURCE 重命名为 RCT_BUILD_HERMES_FROM_SOURCE
    • React-Codegen 重命名为 ReactCodegen,以便更好地兼容 use_frameworksSwift
  5. TextInput:

    • 移除了已弃用的 onTextInput 回调。

鸣谢

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

特别感谢在此版本发布文档中对功能进行了记录的所有额外作者:

  • Nick Gerleman 和 Joe Vilches:为 Yoga 3.1 和布局改进工作。
  • Arushi Kesarwani:为在新架构中支持 UIManager
  • Phillip Pan:为在 TurboModules 中访问 jsi::Runtime
  • Alan Lee 和 Soe Lynn:为最后一个支持 minSdk 23minIOSVersion 13.4 的版本工作。
  • Kudo Chien:为自动链接性能改进。
  • Alex Hunt:为移除 ram-bundleprofile-hermes 命令。

升级至 0.75

请使用 React Native Upgrade Helper 来查看 React Native 版本之间的代码更改,以便为现有项目进行升级,并参考升级文档。

要创建新项目:

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

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