跳到主要内容

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
今天我们发布 React Native 0.74!此版本增加了 Yoga 3.0、新架构下的默认无桥接模式、批量 onLayout 更新(新架构),以及将 Yarn 3 作为新项目的默认包管理器。
Nicola Corti

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

亮点

Yoga 3.0

提高 Android 最低 SDK 版本 (Android 6.0)

Yoga 3.0

新的布局行为

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

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

警告

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

样式

之前之后支持 align-content: 'space-evenly'
<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

Yoga 3.0 支持 alignContent: 'space-evenly'space-evenly 使用均匀分布的间隙来分布多行弹性容器中的行,这些间隙位于行之间和容器边缘之间。

来源:万维网联盟

Visual reference for alignContent behaviors
支持 position: 'static'

信息

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

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

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

<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

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

新架构:默认无桥接

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

无桥接模式并不是我们唯一工作的互操作层:我们还改进了新的渲染器互操作层。最令人兴奋的是它现在默认启用:您无需指定必须经过它的组件!您可以在这里阅读更多关于它们的信息。

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

新架构:批量 onLayout 更新

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

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

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

危险

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

新项目使用 Yarn 3

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

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

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

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

$ yarn <command>

重大变更

提高 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。

移除废弃的 PropTypes

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

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

以下 PropTypes 属性被移除:Image.propTypes, Text.propTypes, TextInput.propTypes, ColorPropType, EdgeInsetsPropType, PointPropType, ViewPropTypes(参阅 commit)。

如果您的应用或库依赖于 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 中移除

为了使用 getInitialNotification() 检索启动应用的通知,您现在需要显式地在 RCTPushNotificationManager 上设置 initialNotification

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

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

[RCTPushNotificationManager setInitialNotification:response.notification];

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

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,
};

💡 如何迁移

iOS

您的 AppDelegate 需要实现 UNUserNotificationCenterDelegate。这应该在应用启动时,即在 application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions: 中完成(更多详情请参阅 Apple 文档)。

实现 userNotificationCenter:willPresentNotification:withCompletionHandler:,当通知到达且应用在前台时会调用此方法。使用 completionHandler 来确定是否应向用户显示通知,并相应地通知 RCTPushNotificationManager

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

return YES;
}

要处理通知被点击时的情况,实现 userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:。请注意,如果您在 userNotificationCenter:willPresentNotification:withCompletionHandler: 中设置了前台通知要显示,您应该只在这些回调中的一个中通知 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);
}

如果点击的通知导致应用启动,调用 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();
}

application:didReceiveLocalNotification: [废弃]

  1. application:didReceiveRemoteNotification: [废弃]
  2. application:didReceiveRemoteNotification:fetchCompletionHandler: [未废弃,但已被 UNUserNotificationCenterDelegate 方法取代]
  3. 删除所有 application:didRegisterUserNotificationSettings: 以及 RCTPushNotificationManager 对应的 didRegisterUserNotificationSettings: 的用法。

示例:参阅 RNTester 的 AppDelegate.mm

JS

移除所有对 alertAction 的引用。

  1. 移除 removeEventListener 调用中的 handler 参数。
  2. 通过使用 fireDatefireIntervalSeconds 触发多个通知来替代所有 repeatInterval 的用法。
  3. 请注意,当从 getScheduledLocalNotifications()getDeliveredNotifications() 返回的 Notification 中访问 soundName 时,它将为 null。
  4. 移除 Flipper React Native 插件

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

移除应用中 Flipper 的差异可以在 Upgrade Helper 中看到。如果您想在现有应用中保留 Flipper,请忽略相关的差异行。

💡 如何重新集成 Flipper

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

我们建议团队投入精力,切换到 Android Studio 和 Xcode 中的原生调试工具。

提示

替代 Flipper

有许多专用的调试工具可以替代 Flipper 的功能。如需更多信息,我们建议阅读 Jamon Holmgren 撰写的优秀文章《为什么你的 React Native 应用不需要 Flipper》。

JavaScript 调试

使用Hermes Debugger 仍然是我们推荐的 0.74 调试选项。您也可以尝试 Experimental New Debugger,它也是 Expo 中的默认调试器。这仍然是一个早期预览版本 — 已知问题和更新可以在这里跟踪。

其他重大变更

通用

让样式中的 start/end 总是指向书写方向(#42251)。

  • Android

FabricUIManagerProvider 移除 JSIModule*#42059)。

从 iOS codegen CLI 删除 configFilenameconfigKey 参数(#41533)。

  • 更改 bundleURL 的处理方式(#43994)。
  • 以前,bundleURL 在 React Native 启动时作为实例变量设置,无法更新。
    • 现在,bundleUrl 是一个函数,需要时会重新评估,允许在刷新时使用不同的 URL。
    • 此更改仅在您在应用启动后更改了 bundleURL 变量时才会影响您的应用。在这种情况下,将更新该变量的逻辑移动到 AppDelegate 中的 bundleURL 函数中。
    • 请参阅完整的 changelog,以获取所有重大变更的完整列表。

已知问题

使用多个窗口时的边缘情况:当主窗口处于非活动状态且系统尝试呈现对话框时,对话框不会显示在屏幕上的正确位置。#44167 中正在提交修复,并将在 0.74.1 中发布。

从 iOS codegen CLI 删除 configFilenameconfigKey 参数(#41533)。

React Native 0.74 包含来自 57 位贡献者的 1673 多次提交。感谢大家的辛勤工作!

感谢所有参与此发布文章特性文档编写的其他作者

Nick Gerleman 负责 Yoga 3.0

请使用 React Native Upgrade Helper 来查看现有项目在不同 React Native 版本之间的代码更改,并参阅Upgrading 文档。

创建新项目

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

npx react-native@latest init MyProject

0.74 现在是 React Native 的最新稳定版本,并且 0.71.x 将不再支持。更多信息请参阅 React Native 的支持政策。我们计划在五月初发布 0.71 的最后一个生命周期结束更新。

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

标签