跳到主要内容

React Native 0.79 - 更快的工具链和更多功能

· 阅读需 10 分钟
Alan Hughes
Alan Hughes
Software Engineer @ Expo
Shubham Gupta
Shubham Gupta
Software Engineer @ Dream11
Fabrizio Cucci
Fabrizio Cucci
Software Engineer @ Meta
Nicola Corti
Nicola Corti
Software Engineer @ Meta

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

该版本在多个方面带来了性能提升,并修复了若干问题。首先,得益于延迟哈希,Metro 启动速度更快,并且对 package exports 提供了稳定支持。Android 启动时间也将因为 JS Bundle 压缩方式的改变等诸多优化而得到提升。

亮点

亮点详解

Metro:更快的启动和 package exports 支持

本次发布包含了 Metro 0.82 版本。该版本引入了延迟哈希技术,提升了首次执行 yarn start 的速度,通常提高3倍以上(大型项目和 monorepos 中提升更明显),为日常开发体验与 CI 构建带来加速。

metro 启动速度对比

另外,在 Metro 0.82 中,我们将 package.json"exports""imports" 字段解析提升为稳定功能。"exports" 功能于 React Native 0.72 引入,"imports" 支持为社区贡献,这两个字段将在 React Native 0.79 所有项目中默认启用。

这提升了与现代 npm 依赖的兼容性,并开辟了符合标准的项目组织方案。

破坏性变更

虽然我们在社区中已经对 package.json"exports" 功能进行了较长时间的测试,但该切换可能对部分包和项目配置造成破坏性变更。

我们特别注意到用户反馈中,一些流行包(如 Firebase 和 AWS Amplify)存在兼容问题,正在努力与相关方修复。

如遇问题:

JSC 迁移至社区包

作为减少 React Native API 面积的工作之一,我们正将 JavaScriptCore (JSC) 引擎迁移至社区维护的包:@react-native-community/javascriptcore

此更改不会影响使用 Hermes 的用户。

从 React Native 0.79 开始,您可通过遵循 社区包的安装说明,使用社区支持的 JSC 版本。React Native 核心提供的 JSC 版本将在 0.79 中继续可用,但 不久后将移除

将 JSC 迁移至社区包使我们能更频繁地更新 JSC 版本,为您提供最新功能。社区维护的 JSC 版本将独立于 React Native 版本更新。

iOS:Swift 兼容的原生模块注册

本次发布重构了将原生模块注册入 React Native runtime 的方式。新方法与组件注册的方式一致,具体可参见官方文档

从本版本起,可以通过修改 package.json 文件注册模块。我们在 ios 配置中引入了新的 modulesProvider 字段:

"codegenConfig": {
"ios": {
+ "modulesProvider": {
+ "模块的 JS 名称": "用于纯 C++ Turbo Module 的 ObjC 模块提供者,或符合 RCTTurboModule 的类"
+ }
}
}

Codegen 会从您的 package.json 自动生成相关代码。

如果您使用纯 C++ 原生模块,建议按以下方式配置:

为纯 C++ 原生模块配置应用

对于纯 C++ 原生模块,您需要添加一个 ObjectiveC++ 类,将 C++ 模块与应用其它部分连接:

CppNativeModuleProvider.h
#import <Foundation/Foundation.h>
#import <ReactCommon/RCTTurboModule.h>

NS_ASSUME_NONNULL_BEGIN

@interface <YourNativeModule>Provider : NSObject <RCTModuleProvider>

@end
CppNativeModuleProvider.mm
NS_ASSUME_NONNULL_END

#import "<YourNativeModule>Provider.h"
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/TurboModule.h>
#import "<YourNativeModule>.h"

@implementation NativeSampleModuleProvider

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeSampleModule>(params.jsInvoker);
}

通过该方法,我们统一了应用开发者与库维护者的原生模块注册方式。库也可在 package.json 中声明相同字段,Codegen 负责其余流程。

此方案解决了 0.77 版本中纯 C++ 原生模块无法在 Swift 实现的 AppDelegate 中注册的限制。正如所示,该改动无需修改 AppDelegate,且生成代码可同时兼容 Swift 与 Objective-C 实现的 AppDelegate

Android:更快的应用启动

我们还带来了显著提升 Android 启动速度的改动。

从本版本起,将不再压缩 APK 内的 JavaScript Bundle。之前,Android 系统需先解压 JS Bundle 才能启动,导致启动变慢。

本次发布默认以未压缩形式打包 JavaScript Bundle,令 Android 应用启动更快。

Margelo 团队 在 Discord 应用上测试该特性,时间响应 (TTI) 缩短了 400ms,相当于 12% 的速度提升(在三星 A14 机型测试,且仅改动一行配置)。

但同时,未压缩 Bundle 会导致应用在设备上的存储空间占用增加。如有空间方面顾虑,可在 app/build.gradle 中的 enableBundleCompression 属性切换此行为:

app/build.gradle
react {
// ...
// 如果希望压缩 JS Bundle(启动较慢,空间占用较小)
enableBundleCompression = true
// 如果不希望压缩 JS Bundle(启动较快,但占用空间较大)
enableBundleCompression = false

// 默认值为 `false`
}

请注意,本次发布 APK 初始大小会增加,但网络下载时 APK 本身仍会被压缩,用户不会因此多消耗下载流量。

破坏性变更

移除远程 JS 调试

为了提升调试体验,我们移除了基于 Chrome 的远程 JS 调试功能。此遗留调试方式已在 React Native 0.73 中被弃用并转为运行时显式启用。请使用更现代且可靠的 React Native DevTools 进行调试。

这也意味着 React Native 不再兼容社区项目 react-native-debugger。想使用第三方调试扩展(如 Redux DevTools)的开发者,建议采用 Expo DevTools 插件 或集成这些工具的独立版本。

详情请参阅 此专项讨论帖子

内部模块更新为 export 语法

为使 JavaScript 代码现代化,我们更新了 react-native 内部若干实现模块,统一使用 export 语法代替 module.exports

共更新约 46 个 API,详情见 变更日志

此更改对现有导入有细微影响:

案例 1:默认导出
  // 变更前 - require 语法
- const ImageBackground = require('react-native/Libraries/Image/ImageBackground');
+ const ImageBackground = require('react-native/Libraries/Image/ImageBackground').default;

// 不变 - import 语法
import ImageBackground from 'react-native/Libraries/Image/ImageBackground';

// 推荐 - 从根路径导入
import {ImageBackground} from 'react-native';

案例 2:二级导出

此类情况较少,使用根路径 'react-native' 导入时无变化。

  // 不变 - require 语法
const BlobRegistry = require('react-native/Libraries/Blob/BlobRegistry');

// 不变 - require 解构语法
const {register, unregister} = require('react-native/Libraries/Blob/BlobRegistry');

// 变更 - import 单对象语法
- import BlobRegistry from 'react-native/Libraries/Blob/BlobRegistry';
+ import * as BlobRegistry from 'react-native/Libraries/Blob/BlobRegistry';


// 不变 - import 解构语法
import {register, unregister} from 'react-native/Libraries/Blob/BlobRegistry';

// 推荐 - 从根路径导入
import {BlobRegistry} from 'react-native';

我们预期该变更影响极小,特别是对于使用 TypeScript 及 import 语法的项目。请检查有无类型错误及时调整代码。

提示

强烈推荐使用根路径 react-native 进行导入

作为通用建议,推荐从根路径 'react-native' 导入,以减少未来可能出现的破坏性变更。在下一版本中,我们将废弃深度导入,旨在更明确 React Native 的公共 JS API 边界(详见RFC 说明)。

其他破坏性变更

以下为疑似对产品代码有轻微影响的其他破坏性变更,值得注意:

  • 无单位的长度值(unitless)在 box-shadow 和 filter 中无效
    • 为增强 React Native 与 CSS/Web 标准的兼容性,现不再支持 box-shadow 和 filter 中无单位的长度值。例如,box-shadow: 1 1 black 不再生效,应指定单位如 1px 1px black
  • 从 normalize-color 中移除错误的 hwb() 语法支持
    • 为了符合 CSS/Web 规范,限制了对非法 hwb() 语法的支持。历史上 React Native 支持逗号分隔形式(如 hwb(0, 0%, 100%)),现已废弃,应改为空格分隔(hwb(0 0% 100%))。详情见 此提交
  • Libraries/Core/ExceptionsManager 导出更新
    • 作为底层 JS API 现代化改进,[ExceptionsManager](https://github.com/facebook/react-native/blob/0.79-stable/packages/react-native/Libraries/Core/ExceptionsManager.js) 现在默认导出一个 ExceptionsManager 对象,并以二级导出形式提供 SyntheticError

致谢

React Native 0.79 汇集了来自 100 名贡献者的 944 次提交。感谢大家的辛勤付出!

特别感谢本次版本中作出重要贡献的社区成员:

同时感谢其他作者对本发布文档的支持:

  • Rob Hogan 撰写“Metro 新特性”章节
  • Alex Hunt 撰写“移除远程 JS 调试”及“内部模块更新为 export 语法”章节
  • Riccardo Cipolleschi 贡献 iOS 原生模块注册部分内容

升级至 0.79

请使用 React Native Upgrade Helper 查看不同版本间的代码变更,以及参考升级文档。

创建新项目请执行:

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

若使用 Expo,React Native 0.79 将作为默认版本包含于即将发布的 Expo SDK 53。

信息

0.79 现为 React Native 最新稳定版,0.76.x 版本转入不再支持。详情见 React Native 支持政策。我们计划在近期发布 0.76 最终终止支持更新。