跳到主要内容

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!我们现在正在从 React Native 中移除所有内置的 PropTypes,从而减小应用大小(缩小包中为 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 Docs)。

- (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 调试

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

其他重大变更

通用

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

Android

  • 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 的最终停止支持更新。