跳到主要内容

React Native 0.77 - 新的样式特性、Android 16KB 页面支持、Swift 模板

· 阅读需 14 分钟
Vojtech Novak
Vojtech Novak
Software Engineer @ Expo
Mazen Chami
Mazen Chami
Software Engineer @ InfiniteRed
Blake Friedman
Blake Friedman
Software Engineer @ Meta
Rob Hogan
Rob Hogan
Software Engineer @ Meta

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

本次发布包含多个特性:新增样式功能,如支持 display: contentsboxSizingmixBlendMode 以及与 outline 相关的属性,提供更强大的布局选项;Android 16KB 页面的支持以兼容更新的安卓设备。我们还通过迁移社区模板到 Swift 来实现现代化,同时继续支持并维护对偏好 Objective-C 的开发者的兼容性。

亮点

重大变更

亮点

更佳布局、尺寸和混合的新 CSS 特性

React Native 0.77 进一步推进了将 React Native 与 Web 对齐的目标。我们添加了对新的 CSS 属性的支持,让你对应用的布局、尺寸和混合效果拥有更多控制。这些改动可帮助简化复杂布局,增加层次感,同时提升应用的可访问性。

信息

所有这些新特性仅适用于新架构

使用 display: contents 简化布局

display: contents 属性允许一个元素在布局结构中“消失”,但其子元素仍像直接子元素一样渲染。它对样式处理非常有用,比如你想对子元素应用样式却不影响布局,或构建必须处理事件的包装组件,或者需要与 ShadowTree 交互时。

技术上讲,display: contents 会渲染元素但不生成布局盒子,同时保留元素子元素的布局盒子。带有 display: contents 的元素实际上从视图层级中被“扁平化”了。

来看一个示例,我们想在某个 widget 被点击时显示警告。容器视图中有一个红色的 Widget

Container.jsx
function Container() {
return (
<View style={styles.container}>
<Widget />
</View>
);
}

display contents - setup

接下来,构建一个新的 Alerting 包装组件,目标是在其下方组件被点击时弹出警告,使用实验性指针事件。为了清晰,这个组件的背景设为蓝色,如下所示:

Container.jsx
function Alerting({children}) {
return (
<View
style={{backgroundColor: 'blue'}}
onPointerDown={() => alert('Hello World!')}>
{children}
</View>
}

function Container() {
return (
<View style={styles.container}>
<Alerting>
<Widget />
</Alerting>
</View>
);
}

但是这并没有达到预期效果。Alerting 新增了一个自己的布局盒子,且有自己的边界,与子 Widget 分开。根据被包装元素的样式,可能导致明显的视觉和功能差异。在此例中,蓝色背景响应点击弹出警告,而我们期望只有红色的 “Hello World” 区域响应点击弹出警告。

before display contents

如果再次尝试,但给 AlertingView 包装设置 display: contents,用户只在点击原 Widget 的边界范围内才触发警告。因为 Alerting 不再添加自身布局盒子,但仍能监听从 Widget 冒泡的指针事件。

Container.jsx
function Alerting({children}) {
return (
<View
style={{display: 'contents'}}
onPointerDown={() => alert('Hello World!')}>
{children}
</View>
);
}

// ... function Container ...

after display contents

盒模型定位(Box sizing)

boxSizing 属性定义元素的各种尺寸属性(widthheightminWidthminHeight 等)如何计算。如果 boxSizing 设为 border-box,尺寸应用于元素的边框盒;如果设为 content-box,尺寸应用于内容盒。默认值为 border-box,这与 Web 上的默认值不同。你可以参考Web 文档了解更多。

注意

border-box 自始至终就是默认值,且在增加了 content-box 之前是唯一的 boxSizing 选项。若更改默认值,将导致破坏性变更和多个布局突然失常。我们决议保留 border-box 作为默认值以确保向后兼容。

下面示例展示 border-boxcontent-box 之间的区别,两者都设置了 padding: 20borderWidth: 10。使用 border-box 时,尺寸包含边框和内边距;使用 content-box 时,仅由内容决定尺寸。

after display contents

CSS 混合模式(mixBlendMode)

mixBlendMode 属性允许你控制元素的颜色如何与其堆叠上下文(stacking context)中的其它元素混合。详细混合函数请参考MDN 文档

为了更精确控制混合范围,我们还支持 isolation 属性。设置 isolation: isolate 将强制该元素形成一个堆叠上下文。这样,你就可以在某些祖先 View 上设置,不让带有 mixBlendMode 的子孙 View 融合超出这个“隔离”视图范围。

mixBlendMode 的取值
  • normal:元素直接绘制于背景顶部,不混合。
  • multiply:源颜色与目标颜色相乘,替换目标颜色。
  • screen:目标和源色的补色相乘,再取补色。
  • overlay:视背景色不同乘或屏幕混合。
  • darken:选取背景色与源色中较暗的颜色。
  • lighten:选取背景色与源色中较亮的颜色。
  • color-dodge:根据源色调整背景色亮度,黑色画笔无效。
  • color-burn:根据源色调整背景色暗度,白色画笔无效。
  • hard-light:根据源色值乘或屏幕混合,类似强光效果。
  • soft-light:根据源色值调暗或调亮,类似柔光效果。
  • difference:用较亮颜色减去较暗颜色。
  • exclusion:类似 difference,但对比度较低。
  • hue:以源色的色相合成颜色,保持背景的饱和度和亮度。
  • saturation:以源色的饱和度合成颜色,保持背景的色相和亮度。
  • color:以源色的色相和饱和度合成颜色,保持背景的亮度(适合彩色单色图像着色)。
  • luminosity:以源色的亮度合成颜色,保持背景的色相和饱和度(与 Color 模式相反效果)。

blend mode

Outline 属性

我们新增了 outlineWidthoutlineStyleoutlineSpreadoutlineColor。这些轮廓属性与相应的 border 属性类似,但它们绘制于边框盒的外侧,而非内边距盒。通过这些属性,可以突出显示元素边框轮廓,而不改变其布局。

详情请参考MDN 文档

outline props

Android 15 版本支持及 16KB 页面支持

Android 15 强制边缘到边缘显示

在上一个版本我们已部分支持 Android 15。Android 15 的一个显著变化是,当构建目标 SDK 版本为 35 时,强制开启边缘到边缘的显示样式。

如果你还未关注此事,请参考我们之前的建议——忽略此点可能导致应用界面崩溃。

备注

若你的应用使用了 react-native-safe-area-context,该库已经帮你处理好边缘到边缘显示的问题。

Android 16 KB 页面大小支持

Android 15 新增了对 16KB 内存页面大小的支持,可带来性能提升,且以前基于 4KB 页面大小的应用可能会在未来设备上不兼容。目前这对开发者是一个可选开关,允许在指定设备上测试以准备未来默认采用 16KB 页面大小。

自 0.77 版本起,React Native 已做好全面支持 16KB 页面大小,开发者即可测试并发布支持该页面大小的应用。

更多信息请查阅官方 Android 开发者网站

社区 CLI 与模板更新

社区 CLI:废弃 react-native init

此版本完成了自 React Native 0.75 起开始废弃的 react-native init 命令。

提醒一下,你将不能再使用 react-native init,需选择:

  • 使用框架 例如 Expo,自身带有独立的新建项目命令:npx create-expo-app
  • 直接调用社区 CLI:npx @react-native-community/cli init

社区 CLI:从 Metro 移除 iOS/Android 运行的快捷键

本版本中我们从 Metro 移除了 ‘a’ 和 ‘i’ 快捷键,这些快捷键原本用于调用社区 CLI 的 run-androidrun-ios 命令。此快捷键体验较差且不常用,我们认为由框架负责统一终端输出更为合理。

关于此变更详情,可见专门帖文

社区模板:iOS 使用 Swift 作为主语言

信息

使用 Expo 的项目不会受到此项更改影响。

此次更改通过将三个文件 (main.mAppDelegate.hAppDelegate.mm) 替换为一个新的 AppDelegate.swift,大幅精简了社区模板。

这技术上属于破坏性变更:升级助手中你会看到从 Objective-C 切换到 Swift 的提示,如图:

Swift Upgrade Helper

你不必迁移到 Swift:iOS 社区模板的 Objective-C++ 版本仍受支持(需集成 RCTAppDependencyProvider)。新建项目默认使用 Swift 作为 iOS 语言,当然你也可以随时迁回 Objective-C。

限制

如果你的应用含有用 C++ 编写的本地模块,无法用 Swift 注册它们,详见此指南

此类项目请跳过 AppDelegate 迁移,继续用 Objective-C++。

React Native 核心多用 C++ 开发,鼓励 iOS、Android 等平台共享代码。Swift 与 C++ 的互操作性尚不成熟稳定,我们正在寻找解决方案,未来支持 Swift 迁移。

RCTAppDependencyProvider

React Native 0.77 稍作调整了应用加载第三方依赖方式。社区模板中新增了类似代码,若遗漏可能引发运行时问题,请确保添加。

Objective-C 相应代码如下:

AppDelegate.mm
#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"<Your app Name>";
self.dependencyProvider = [RCTAppDependencyProvider new];
// 你可以在下面字典中添加自定义初始属性。
// 它们会传递给 React Native 使用的 ViewController。
self.initialProps = @{};

return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

// 剩余的 AppDelegate 代码

重大变更

Metro 移除 console.log() 流式传输

我们希望 React Native 的调试环节可靠且符合现代浏览器工具标准。为此,0.77 版本开始移除此前在 0.76 版本弃用的 Metro 内日志转发功能。

此功能基于与设备上调试目标的自定义通信协议。现在,我们彻底转向 Chrome DevTools 协议(CDP)。

  • 请使用 React Native DevTools 的丰富控制台面板查看 JS 日志,支持日志过滤、丰富对象检查、实时表达式等。
  • 你也可以通过第三方扩展(如 Expo ToolsRadon IDE)将 VS Code 连接为 CDP 调试器。
    • 这些集成暂不由 React 团队直接支持,我们正计划在 2025 年推出官方 VS Code 支持。
  • Expo CLI 仍提供日志流式传输功能。

详情请参阅 为何 JavaScript 日志离开 Metro?

其他重大变更

通用

  • 动画
    • 原生循环动画不再在每次循环结束时发送 React 状态更新。
  • 布局
    • 现在支持考虑 ScrollView 中粘性标题的 position
    • 绝对定位表现更符合规范。
  • JS 模块
    • 移除 ReactFabricInternals 模块,不再可访问。
  • 原生模块
    • 现在可通过 NativeModules 载入 TurboModules,提升原生模块与 Turbo Native Modules 兼容性。
  • 包管理
    • dev-middleware:框架应以 middleware 主机为相对路径指定 serverBaseUrl
  • API 变更
    • AppRegistry 中移除 useConcurrentRoot 类型,因已被忽略。
    • NativeMethods TypeScript 定义中移除 refs 属性。
  • 用户体验
    • 移除开发服务器的 “run on iOS” 和 “run on Android” 快捷键命令。

Android

  • Kotlin
    • 这是首个基于 Kotlin 2.0.21 构建的 React Native 版本。你可以在语言发行说明了解 Kotlin 2.0 相关改动。
  • API 变更
    • 可空性
      • ReadableArray 中非原始类型的 getter 现在被正确标记为可选。
      • ReactHost.createSurface() 方法改为非空返回。
    • 重命名
      • DevSupportManagerBase.getCurrentContext() 改名为 DevSupportManagerBase.getCurrentReactContext()

此外,多个 API 被移除或限制可访问,不能再使用。这些是内部 API,非 React Native 开发者直接使用。完整清单请见下面:

被移除的 Android API 列表:

以下包已内部化,不可访问:

  • com.facebook.react.views.progressbar
  • com.facebook.react.views.safeareaview
  • com.facebook.react.modules.accessibilityinfo
  • com.facebook.react.modules.appstate
  • com.facebook.react.modules.clipboard
  • com.facebook.react.modules.devmodule
  • com.facebook.react.modules.reactdevtoolssettings
  • com.facebook.react.views.unimplementedview

以下类已内部化或被移除,无法访问:

  • BackHandler.removeEventListener
  • BaseViewManagerInterface
  • BindingImpl
  • CompositeReactPackage
  • DebugOverlayTags
  • DefaultDevSupportManagerFactorycreate() 方法
  • DevToolsReactPerfLogger
  • FabricComponents
  • ImageStoreManager
  • InteropModuleRegistry
  • NativeModulePerfLogger
  • NoopPrinter
  • NotThreadSafeViewHierarchyUpdateDebugListener
  • OkHttpCallUtil
  • PrinterHolder
  • Printer
  • ReactDebugOverlayTags
  • ReactNativeFlipper
  • ReactViewBackgroundManager
  • ReactViewGroup.getBackgroundColor()
  • ReactVirtualTextShadowNode
  • ReactVirtualTextViewManager
  • SimpleSettableFuture
  • SwipeRefreshLayoutManager
  • TaskCompletionSource
  • DefaultReactHost.getDefaultReactHost()jsBundleLoader 参数

iOS

  • API 变更
    • 移除
      • RCTConstants.RCTGetMemoryPressureUnloadLevel
      • partialBatchDidFlush
      • RCTRuntimeExecutor
      • UseNativeViewConfigsInBridgelessMode —— 替换为合适的功能标记
      • UseTurboModuleInteropForAllTurboModules —— 互操作层始终开启
    • 改动
      • 将所有 CGColorRef 替换为 UIColor
  • RCTAppDelegate 现在要求通过 RCTDependencyProvider 加载第三方依赖。
  • CocoaPods 为所有第三方依赖设置 C++ 版本,避免编译问题。
React 19?

React 19 于 2024 年 12 月 6 日发布。

当时,我们已为 React Native 0.77 开启分支,并发布了三个 RC 版本。

故在 React Native 0.77 版本中引入 React 19 已为时过晚。

React 19 将随 React Native 0.78 一同推出,我们已为此版本开通分支。你可以使用以下命令创建基于 React 19 的新应用:

npx @react-native-community/cli init YourReact19App --version 0.78.0-rc.0

致谢

React Native 0.77 包含 1061+ 个提交,贡献者共计 161 人。感谢大家的辛勤付出!

感谢如下额外作者为本次发布文章贡献文档:

升级至 0.77

请使用 React Native 升级助手 查看现有项目在 React Native 版本间的代码变更,配合官方升级文档使用。

新建项目:

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

若使用 Expo,React Native 0.77 将在 Expo SDK 52 支持(关于如何在 Expo 项目中升级至 React Native 0.77.0 的详细说明将于近期单独发布 Expo 博客)。

信息

0.77 现为 React Native 最新稳定版本,0.74.x 转为不再支持。详情参见 React Native 支持政策。我们计划在近日发布 0.74 的最终维护更新。