跳到主要内容

迈向稳定的 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 类型和新的公共 API 基线(TypeScript)。这将实现更强大、更具未来适应性的类型准确性,并且将是一次性破坏性更改。通过项目 tsconfig.json 中的 compilerOptions 选择加入
  • 我们将与社区长期合作,确保这些更改适用于所有人,然后才会在未来的 React Native 版本中默认启用严格 TypeScript API。

变化内容及原因

我们正在改进和稳定 React Native 的公共 JavaScript API——即您通过 `import '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/` 目录解析 `react-native` 类型,而不是以前的手动维护的 `types/` 目录。不需要重启 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` 只会影响当前的 codebase。

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

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

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

遗憾的是,还不能。在 0.80 版本中,我们进行了一项工具投资,以便可以通过 TypeScript 精确地使用 React Native 现有的 JS API 基线——从而实现未来的稳定更改。我们正在正式化您所熟悉和喜爱的现有 API。

将来,我们将采取行动来正式化我们目前在核心中提供的 API——跨越每种语言表面。API 更改将通过 RFC/公告进行沟通,通常会有一个弃用周期。

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

React Native 是 Meta 的核心基础设施。我们在每次合并更改后,都会在我们的 Family of Apps 中进行测试,然后再将其公开提供给广大开源社区。

在这种规模和敏感性下,正确性至关重要。底线是,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