跳到主要内容

React Native 0.74 - Yoga 3.0、无桥接新架构等

·15分钟阅读
Hur Ali
Hur Ali
Callstack 软件工程师
Alan Hughes
Alan Hughes
Expo 软件工程师
Alfonso Curbelo
Alfonso Curbelo
Coinbase 软件工程师
Alex Hunt
Alex Hunt
Meta 软件工程师
Nicola Corti
Nicola Corti
Meta 软件工程师

今天我们发布 React Native 0.74!此版本增加了 Yoga 3.0,在新架构下默认启用无桥接模式,批量 onLayout 更新(新架构),以及 Yarn 3 作为新项目的默认包管理器。

我们还移除了废弃的 API,包括 PropTypes 的移除和 PushNotificationIOS 的重大变更。在 Android 上,SDK 23(Android 6.0)现在是最低支持版本。

亮点

破坏性变更

亮点

Yoga 3.0

新的布局行为

React Native 0.74 包含了 Yoga 3.0,这是我们布局引擎的最新版本。Yoga 3.0 通过使样式更具可预测性来改进布局,并支持渲染为 Web 编写的组件。

React Native 会继续有意保留一些不正确的布局行为,因为发现修复它们会影响大量实际组件。未来的 React Native 版本将能够更细粒度地配置布局一致性。

警告

React Native 之前在处理设置在 row-reverse 容器上的 marginpaddingborder 时,会翻转 left/right(以及 start/end)边缘。现在,这些属性的行为与 Web 对齐。之前依赖边缘反转的代码可能需要更新才能继续正确渲染。

样式之前之后
<View
style={{
flexDirection: 'row',
backgroundColor: 'red',
margin: 10,
width: 200,
height: 100,
}}>
<View
style={{
flexDirection: 'row-reverse',
backgroundColor: 'blue',
flex: 1,
marginLeft: 50,
}}>
<View
style={{
backgroundColor: 'green',
height: '50%',
flex: 1,
marginLeft: 50,
}}
/>
</View>
</View>

Previous layout

New layout

支持 align-content: 'space-evenly'

Yoga 3.0 带来了对 alignContent: 'space-evenly' 的支持。space-evenly 使用均匀间隔的间隙在多行 flex 容器中分布线条,这些间隙位于线条和容器边缘之间。

Visual reference for alignContent behaviors
来源:万维网联盟

支持 position: 'static'

信息

position: 'static' 仅在新架构中受支持。

标记为 position: 'static' 的元素可能不会被偏移,并且在确定绝对定位元素的 包含块 时不会被考虑。这允许将元素相对于其祖先(而非其直接父级)进行定位。

<View
style={{
backgroundColor: 'blue',
width: 200,
height: 200,
flexDirection: 'row-reverse',
}}>
<View
style={{
backgroundColor: 'red',
width: 100,
height: 100,
position: 'static',
}}>
<View
style={{
backgroundColor: 'green',
width: 25,
height: '25%',
left: 25,
top: 25,
position: 'absolute',
}}
/>
</View>
</View>

Static Example

请注意,绿色的 <View> 如何声明 lefttop,并且它相对于蓝色的 <View> 定位,而不是其父级。

未设置 position 时,React Native 仍默认为 position: 'relative'

新架构:默认无桥接

在此版本中,当启用新架构时,我们将无桥接模式设为默认。您可以在此帖子中了解更多关于我们切换到默认无桥接的信息。为了使过渡更顺畅,我们增强了互操作层以覆盖无桥接,并与多个库合作以确保它们从第一天起就能在无桥接模式下工作。

无桥接不是我们唯一在研究的互操作层:我们也改进了新渲染器互操作层。最令人兴奋的是它现在默认启用:您无需指定必须通过它的组件!您可以在此处阅读更多相关信息。

最后,如果您想了解更多关于新架构的信息,可以在 react-native-new-architecture 仓库中找到文档。当新架构成为默认时,这些信息将被整合到 reactnative.dev

新架构:批量 onLayout 更新

onLayout 回调中的状态更新现在是批处理的。以前,onLayout 事件中的每个状态更新都会导致一个新的渲染提交。

function MyComponent(props) {
const [state1, setState1] = useState(false);
const [state2, setState2] = useState(false);

return (
<View>
<View
onLayout={() => {
setState1(true);
}}>
<View
onLayout={() => {
// When this event is executed, state1's new value is no longer observable here.
setState2(true);
}}>
</View>
</View>
);
}

在 0.74 中,setState1setState2 更新被批量处理。此更改是 React 中的预期行为,并允许减少重新渲染。

危险

此更改可能会破坏依赖非批处理状态更新的代码。您需要重构此代码以使用 更新函数 或等效方法。

新项目使用 Yarn 3

Yarn 3 现在是使用 React Native Community CLI 初始化新项目的默认 JavaScript 包管理器。

Yarn 3.x 将与 nodeLinker: node-modules 一起使用,这是一种提供与 React Native 库兼容性的模式。它取代了以前的默认 Yarn Classic(1.x,已废弃)。要升级现有应用程序中的 Yarn 版本,您可以遵循此指南

$ yarn --help
━━━ Yarn Package Manager - 3.6.4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

$ yarn <command>

Community CLI 还支持通过 --pm 标志初始化带有其他包管理器的项目(阅读更多)。

重大更改

Android 最低 SDK 提升(Android 6.0)

React Native 0.74 要求最低 Android SDK 版本为 23(Android 6.0)。以前,这个版本是 Android 5.0(API 21)。请参阅此处了解此更改的背景信息。

额外福利:Android 应用大小减小

最低 SDK 的提升,加上我们原生构建的几项改进,使得我们能够大大减少用户设备上的应用程序大小。

例如,使用 React Native 0.74 创建的新应用程序在用户设备上占用的空间减少了约 13%,相当于在设备上节省了约 4MB。

Side-by-side comparison of a new React Native app in the Android system storage view

移除废弃的 PropTypes

在 0.74 之前,React Native 继续附带 PropTypes,这是一个自 2017 年 React 15.5 以来就已废弃的 API!我们现在将所有内置的 PropTypes 从 React Native 中移除,从而减少应用程序大小(在压缩包中减少 26.4kB)和内存开销。

以下 PropTypes 属性已移除:Image.propTypesText.propTypesTextInput.propTypesColorPropTypeEdgeInsetsPropTypePointPropTypeViewPropTypes(参见提交)。

如果您的应用程序或库依赖于 PropTypes,我们强烈建议迁移到 TypeScript 等类型系统。

PushNotificationIOS 的 API 更改(已废弃)

在 React Native 0.74 中,我们正在着手移除已废弃的 PushNotificationIOS 库。此版本中的更改主要集中在移除对旧 iOS API 的引用。PushNotificationIOS 已迁移到 Apple 的 User Notifications 框架,并公开了用于调度和处理通知的新 API。

在下一个版本(0.75)中,我们计划**移除此库**,将其从 React Native 核心中移出,并放入社区包 @react-native-community/push-notification-ios 中。如果您仍然依赖 PushNotificationIOS,则需要在下一个版本发布之前进行迁移。

API 变更

RCTPushNotificationManager 上的 didRegisterUserNotificationSettings: 回调是一个空操作,已被删除。

RCTPushNotificationManager 上的以下回调已废弃,并将在 0.75 中移除

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

要使用 getInitialNotification() 检索启动应用程序的通知,您现在需要明确在 RCTPushNotificationManager 上设置 initialNotification

[RCTPushNotificationManager setInitialNotification:response.notification];

在 JS 方面,Notification 上的属性已更改。alertActionrepeatInterval 现在已废弃,并将在 0.75 中移除

type Notification = {
...
// NEW: Seconds from now to display the notification.
fireIntervalSeconds?: ?number,

// CHANGED: Used only for scheduling notifications. Will be null when
// retrieving notifications using `getScheduledLocalNotifications` or
// `getDeliveredNotifications`.
soundName?: ?string,

// DEPRECATED: This was used for iOS's legacy UILocalNotification.
alertAction?: ?string,

// DEPRECATED: Use `fireDate` or `fireIntervalSeconds` instead.
repeatInterval?: ?string,
};

最后,PushNotificationIOS.removeEventListener 上的 handler 参数未使用,已被移除。

💡 如何迁移

iOS

您的 AppDelegate 将需要实现 UNUserNotificationCenterDelegate。这应该在应用程序启动时在 application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions: 中完成(有关更多详细信息,请参阅 Apple 文档)。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;

return YES;
}

实现 userNotificationCenter:willPresentNotification:withCompletionHandler:,该方法在通知到达且应用程序处于*前台*时调用。使用 completionHandler 确定通知是否将显示给用户,并相应地通知 RCTPushNotificationManager

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
// This will trigger 'notification' and 'localNotification' events on PushNotificationIOS
[RCTPushNotificationManager didReceiveNotification:notification];
// Decide if and how the notification will be shown to the user
completionHandler(UNNotificationPresentationOptionNone);
}

要处理通知被点击时的情况,请实现 userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:。请注意,如果您将前台通知设置为在 userNotificationCenter:willPresentNotification:withCompletionHandler: 中显示,则应仅在这些回调中的一个中通知 RCTPushNotificationManager

如果点击的通知导致应用程序启动,请调用 setInitialNotification:。如果通知之前未被 userNotificationCenter:willPresentNotification:withCompletionHandler: 处理,也请调用 didReceiveNotification:

- (void)  userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
// This condition passes if the notification was tapped to launch the app
if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
// Allow the notification to be retrieved on the JS side using getInitialNotification()
[RCTPushNotificationManager setInitialNotification:response.notification];
}
// This will trigger 'notification' and 'localNotification' events on PushNotificationIOS
[RCTPushNotificationManager didReceiveNotification:response.notification];
completionHandler();
}

最后,删除以下方法并将逻辑适配到上面将代替调用的回调中

  1. application:didReceiveLocalNotification: [已废弃]
  2. application:didReceiveRemoteNotification: [已废弃]
  3. application:didReceiveRemoteNotification:fetchCompletionHandler: [未废弃,但已被 UNUserNotificationCenterDelegate 方法取代]

同时删除 application:didRegisterUserNotificationSettings:RCTPushNotificationManager 相应 didRegisterUserNotificationSettings: 的所有用法。

示例: 请参阅 RNTester 的 AppDelegate.mm

JS

  1. 删除所有对 alertAction 的引用。
  2. 删除 removeEventListener 所有调用中的 handler 参数。
  3. 将所有 repeatInterval 的用法替换为使用 fireDatefireIntervalSeconds 发送多个通知。
  4. 请注意,从 getScheduledLocalNotifications()getDeliveredNotifications() 返回的 Notification 访问 soundName 时将为 null。

移除 Flipper React Native 插件

现在不再支持使用 Flipper 检查 React Native 布局、网络请求和 其他 React Native 插件功能。在 0.74 中,我们已从新的 React Native 项目中移除了原生 Flipper 库和设置代码。这意味着更少的依赖项和更快的本地设置(请参阅 原始 RFC)。

您可以在 升级助手 中查看从应用程序中移除 Flipper 的差异。如果您想在现有应用程序中保留 Flipper,请忽略相关的差异行。

💡 如何重新集成 Flipper

Flipper 仍然可以作为独立的工具用于调试 Android 或 iOS 应用程序,并且可以通过遵循 Flipper 文档手动集成(Android 指南iOS 指南)。

我们建议团队投资于转向 Android Studio 和 Xcode 中的原生调试工具。

提示

替代 Flipper

有许多专用的调试工具可以替代 Flipper 的功能。更多信息,我们推荐阅读 Jamon Holmgren 的优秀文章 为什么您的 React Native 应用程序不需要 Flipper,以及如何在没有它的情况下生存

JavaScript 调试

在 0.74 中,我们仍然推荐使用 Hermes 调试器 作为调试选项。您也可以尝试 实验性新调试器,它也是 Expo 的默认调试器。这仍处于早期预览阶段 — 已知问题和更新可以在此处跟踪。

其他破坏性变更

通用

  • 使样式中的 start/end 始终指代书写方向(#42251)。

安卓

  • FabricUIManagerProvider 中移除 JSIModule*#42059)。
    • 此 API 在开源中未使用 — 请改用 TurboModules
  • 废弃 UIManagerModule.showPopupMenuUIManagerModule.dismissPopupMenu#42441

iOS

  • 删除 iOS codegen CLI 中的 configFilenameconfigKey 参数(#41533)。
  • 更改 bundleURL 的处理方式(#43994)。
    • 在此之前,bundleURL 在 React Native 启动时作为实例变量设置,无法更新。
    • 现在,bundleUrl 是一个函数,它在需要时重新评估,从而允许在刷新时使用不同的 URL。
    • 此更改仅在您在应用程序启动后更改 bundleURL 变量时才会影响您的应用程序。在这种情况下,请将更新变量的逻辑移动到 AppDelegate 中的 bundleURL 函数 中。

请参阅完整更新日志以获取所有重大更改的完整列表。

已知问题

iOS

  • 使用多窗口时的边缘情况:当主窗口不活动且系统尝试显示对话框时,对话框未在屏幕的正确位置显示。修复正在 #44167 中进行,并将在 0.74.1 中发布。

致谢

React Native 0.74 包含来自 57 位贡献者的 1673 多个提交。感谢你们所有的辛勤工作!

感谢所有为本版本发布文章撰写文档的额外作者

升级到 0.74

请使用 React Native 升级助手 查看现有项目在 React Native 版本之间的代码更改,此外还有升级文档

要创建新项目

npx react-native@latest init MyProject

如果您使用 Expo,React Native 0.74 将在 Expo SDK 51 中得到支持。

信息

0.74 现在是 React Native 的最新稳定版本,**0.71.x 不再受支持**。有关更多信息,请参阅 React Native 的支持政策。我们计划在 5 月初发布 0.71 的最终停止支持更新。