跳到主要内容

迈向稳定的 JavaScript API(0.80 中的新变化)

·10 分钟阅读
Alex Hunt
Alex Hunt
Meta 软件工程师
Iwo Plaza
Iwo Plaza
软件工程师 @ Software Mansion
Jakub Piasecki
Jakub Piasecki
软件工程师 @ Software Mansion
Dawid Małecki
Dawid Małecki
软件工程师 @ Software Mansion

在 React Native 0.80 中,我们将对 React Native 的 JavaScript API 进行两项重大更改——弃用深层导入和新的严格 TypeScript API。这些都是为了准确定义我们的 API 并向用户和框架提供可靠的类型安全而持续努力的一部分。

快速要点

  • 深度导入弃用:从 0.80 版本开始,我们将对来自 react-native 包的深度导入引入弃用警告。
  • 选择性加入严格 TypeScript API:我们正在转向从源代码生成 TypeScript 类型和基于 TypeScript 的新公共 API 基线。这将实现更强、更具未来保障的类型准确性,并且将是一次性破坏性更改。通过项目中的 tsconfig.json 文件中的 compilerOptions 选择加入
  • 我们将与社区长期合作,确保这些更改适用于所有人,然后才会在未来的 React Native 版本中默认启用严格 TypeScript API。

变化和原因

我们正在改进和稳定 React Native 的公共 JavaScript API——即您导入 'react-native' 时所获得的内容。

从历史上看,我们一直在近似这种做法。React Native 是用 Flow 编写的,但社区早已在开源中转向 TypeScript,这也是公共 API 被使用和验证兼容性的方式。我们的类型是(充满爱地)社区贡献的,并已合并并与我们的代码库对齐。然而,这些都依赖于手动维护,并且没有自动化工具,从而引入了正确性差距。

此外,我们的公共 JS API 在模块边界方面定义不清——例如,内部的 'react-native/Libraries/' 深层导入可由应用程序代码访问,但随着我们更新这些内部模块,它们可能会频繁更改。

在 0.80 版本中,我们通过弃用深层导入并引入用户选择加入的新的、生成的 TypeScript API 基线来解决这些问题。我们将其称为 严格 TypeScript API。最终,这是未来提供稳定 React Native API 的基础。

弃用 react-native 中的深层导入

我们今天对 API 进行的主要更改是弃用深层导入(RFC),并在 ESLint 和 JS 控制台中显示警告。值和类型的深层导入应更新为 react-native 的根导入。

// Before - import from subpath
import {Alert} from 'react-native/Libraries/Alert/Alert';

// After - import from `react-native`
import {Alert} from 'react-native';

此更改将我们的 JavaScript API 的总表面积缩小到一组固定的导出,我们可以在未来的版本中控制并使其稳定。我们计划在 0.82 中移除这些导入路径。

API 反馈

某些 API 未在根目录导出,并且在没有深层导入的情况下将不可用。我们有一个开放的反馈线程,并将与社区合作以最终确定我们公共 API 中的导出。请分享您的反馈!

选择退出

请记住,我们计划在未来的版本中从 React Native 的 API 中移除深层导入,这些导入应该改为根导入。

选择退出警告

ESLint

使用 overrides 禁用 no-deep-imports 规则。

.eslintrc.js
  overrides: [
{
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
rules: {
'@react-native/no-deep-imports': 0,
},
},
]

控制台警告

disableDeepImportWarnings 选项传递给 @react-native/babel-preset

babel.config.js
module.exports = {
presets: [
['module:@react-native/babel-preset', {disableDeepImportWarnings: true}]
],
};

使用 --reset-cache 重新启动您的应用程序以清除 Metro 缓存。

npx @react-native-community/cli start --reset-cache
选择退出警告(Expo)

ESLint

使用 overrides 禁用 no-deep-imports 规则。

.eslintrc.js
overrides: [
{
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
rules: {
'@react-native/no-deep-imports': 0,
},
},
];

控制台警告

disableDeepImportWarnings 选项传递给 babel-preset-expo

babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [['babel-preset-expo', {disableDeepImportWarnings: true}]],
};
};

使用 --clear 重新启动您的应用程序以清除 Metro 缓存。

npx expo start --clear

严格 TypeScript API(选择性加入)

严格 TypeScript API 是 react-native 包中的一组新的 TypeScript 类型,可以通过 tsconfig.json 选择加入。我们正在随现有 TS 类型一起发布这些类型,这意味着您可以在准备就绪时选择迁移。

新类型是

  1. 直接从我们的源代码生成——提高了覆盖率和正确性,因此您可以期待更强的兼容性保证。
  2. 限制在 react-native 的索引文件——更严格地定义我们的公共 API,这意味着在进行内部文件更改时,我们不会破坏 API。

当社区准备就绪时,严格 TypeScript API 将在未来成为我们的默认 API——与深层导入的移除同步。这意味着现在开始选择加入是个好主意,因为您将为 React Native 未来的稳定 JS API 做好准备。

tsconfig.json
{
"extends": "@react-native/typescript-config",
"compilerOptions": {
...
"customConditions": ["react-native-strict-api"]
}
}
幕后

这将指示 TypeScript 从我们新的 types_generated/ 目录而不是之前的 types/ 目录(手动维护)解析 react-native 类型。不需要重新启动 TypeScript 或您的编辑器。

破坏性变更:不允许深层导入

如上所述,严格 TypeScript API 下的类型现在只能从主要的 'react-native' 导入路径解析,这强制执行了包封装,符合我们上述的弃用。

// Before - import from subpath
import {Alert} from 'react-native/Libraries/Alert/Alert';

// After - MUST import from `react-native`
import {Alert} from 'react-native';
关键优势

我们将公共 API 范围限定在 React Native 的 index.js 文件的导出中,我们对此进行了精心维护。这意味着我们代码库中其他地方的文件更改将不再是破坏性更改。

破坏性变更:某些类型名称/形状已更改

类型现在从源代码生成,而不是手动维护。在这样做时:

  • 我们已经解决了社区贡献类型中出现的差异——并且还增加了我们源代码的类型覆盖率。
  • 我们有意更新了一些类型名称和类型形状,以便在有空间简化或减少歧义时进行更改。
关键优势

由于类型现在是从 React Native 的源代码生成的,因此您可以确信类型检查器对于给定版本的 react-native 始终是准确的

示例:更严格的导出符号

Linking API 现在是一个单一的 interface,而不是两个导出。这适用于许多其他 API(请参阅文档)。

// Before
import {Linking, LinkingStatic} from 'react-native';

function foo(linking: LinkingStatic) {}
foo(Linking);

// After
import {Linking} from 'react-native';

function foo(linking: Linking) {}
foo(Linking);

示例:修复/更完整的类型

以前的手动类型定义留下了类型空白的机会。在生成 Flow → TypeScript 后,这些不再存在(并且在源代码中,受益于 Flow 对多平台代码的额外类型验证)。

import {Dimensions} from 'react-native';

// Before - Type error
// After - number | undefined
const {densityDpi} = Dimensions.get();

其他重大更改

请参阅我们文档中的专门指南,其中详细介绍了所有破坏性类型更改以及如何更新您的代码。

发布

我们理解 React Native 的任何重大变更都需要开发人员花时间在他们的应用程序中进行更新。

现在——选择性加入启动(0.80)

"react-native-strict-api" 选择性加入在 0.80 版本中是稳定的。

  • 这是一次性迁移。我们旨在让应用程序和库在接下来的几个版本中按自己的节奏选择加入。
  • 无论在哪种模式下,您的应用程序在运行时都不会发生任何变化——这仅影响 TypeScript 分析。
  • 并且,我们将通过我们的专门反馈线程收集关于缺失 API 的反馈。
推荐

严格 TypeScript API 将在未来成为我们的默认 API。

如果您有时间,现在就值得在您的 tsconfig.json 中测试选择加入,以确保您的应用程序或库具有前瞻性。这将立即评估在严格 API 下您的应用程序中是否引入了任何类型错误。可能没有(!)——在这种情况下,您就可以开始了。

未来——默认严格 TypeScript API

将来,我们将要求所有代码库都使用我们的严格 API,并将移除旧版类型。

时间表将根据社区反馈确定。至少在接下来的两个 React Native 版本中,严格 API 仍将是选择性加入的。

常见问题

我目前正在使用子路径导入。我该怎么办?

请迁移到根 'react-native' 导入路径。

  • 子路径导入(例如 'react-native/Libraries/Alert/Alert')正在成为私有 API。如果不阻止访问 React Native 内部的实现文件,我们就无法提供稳定的 JavaScript API。
  • 我们希望我们的弃用警告能够激发社区反馈,如果您认为我们没有公开对您的应用程序至关重要的代码路径,可以通过我们的集中讨论线程提出。如果合理,我们可能会将 API 提升到索引导出。

我是一名库维护者。此更改对我有什么影响?

应用程序和库都可以按照自己的节奏选择加入,因为 tsconfig.json 只会影响直接代码库。

  • 通常,在 React Native 项目中,node_modules 会被 TypeScript 服务器排除在验证之外。因此,您的包导出的类型定义是真实来源。

💡 我们需要反馈!与子路径导入更改一样,如果您遇到任何与严格 API 的集成问题,请在 GitHub 上告诉我们

这是否保证了 React Native 的最终 API?

遗憾的是,还没有。在 0.80 版本中,我们投入了工具,以便 React Native 现有的 JS API 基线可以通过 TypeScript 准确消费——从而实现未来的稳定更改。我们正在规范化您熟悉并喜爱的现有 API。

未来,我们将采取行动最终确定我们目前在核心中提供的 API——跨越每个语言层面。API 更改将通过 RFC/公告以及通常的弃用周期进行传达。

为什么 React Native 不是用 TypeScript 编写的?

React Native 是 Meta 的核心基础设施。我们会在每个合并的更改发布到开源之前,在我们的应用程序系列中进行测试。

在这种规模和敏感度下,正确性至关重要。底线是 Flow 为我们提供了比 TypeScript 更高的性能和更高的严格性,包括对 React Native 的特定多平台支持

致谢

这些更改由 Iwo PlazaJakub PiaseckiDawid MałeckiAlex HuntRiccardo Cipolleschi 实现。

还要感谢 Pieter VanderwerffRubén NorteRob Hogan 的额外帮助和建议。

了解更多

观看讲座! 我们在 App.js 2025 上深入探讨了我们对严格 TypeScript API 的动机和幕后工作。 在 YouTube 上观看

App.js 2025 Talk