Skip to main content

库的先决条件


提醒

本文档仍为 experimental,详细信息可能会随着我们的迭代而发生变化。 欢迎在此页面分享你对 工作组内部讨论 的反馈。

This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.

而且,它还包含几个 手动步骤。 请注意,一旦新架构稳定,这将不代表最终的开发者体验。 我们正在开发工具、模板和库,以帮助你快速开始使用新架构,而无需完成整个设置。

Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.

以下步骤将有助于确保你的模块和组件为新架构做好准备。

The following steps will help ensure your modules and components are ready for the New Architecture.

在 JavaScript 中定义规范

JavaScript 规范是每个原生模块提供的方法的真实来源。 它们定义了原生模块提供的所有 API,以及这些常量和函数的类型。 使用 typed 规范文件可以让你有意识地声明原生模块方法的所有输入参数和输出。

The JavaScript specs serve as the source of truth for the methods provided by each native module. They define all APIs provided by the native module, along with the types of those constants and functions. Using a typed spec file allows you to be intentional and declare all the input arguments and outputs of your native module’s methods.

信息

TypeScript 支持目前处于测试阶段。

要采用新架构,你首先要为原生模块和原生组件创建这些规范。 你可以在迁移到新架构之前执行此操作: 稍后将使用该规范为所有受支持的平台生成原生接口代码,作为跨平台强制执行统一 API 的一种方式。

To adopt the New Architecture, you start by creating these specs for your native modules and native components. You can do this before migrating to the New Architecture: the specs will be used later on to generate native interface code for all the supported platforms as a way to enforce uniform APIs across platforms.

Turbo Native 模块

JavaScript 规范文件 must 被命名为 Native<MODULE_NAME>.js,并且它们导出 TurboModuleRegistry Spec 对象。 名称约定很重要,因为 Codegen 进程会查找 jsjsxtstsx)规范文件以关键字 Native 开头的模块。

JavaScript spec files must be named Native<MODULE_NAME>.js, and they export a TurboModuleRegistry Spec object. The name convention is important because the Codegen process looks for modules whose js (jsx, ts, or tsx) spec file starts with the keyword Native.

以下是一个基本的 JavaScript 规范模板,使用 FlowTypeScript 语法编写。

The following is a basic JavaScript spec template, written using the Flow and TypeScript syntax.

// @flow strict

import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import {TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
+getConstants: () => {||};

// your module methods go here, for example:
getString(id: string): Promise<string>;
}

export default (TurboModuleRegistry.get<Spec>(
'<MODULE_NAME>',
): ?Spec);

Fabric 原生组件

JavaScript 规范文件 must 被命名为 <FABRIC COMPONENT>NativeComponent.js(对于 TypeScript 使用扩展名 .ts.tsx),并且它们导出 HostComponent 对象。 命名约定很重要: Codegen 进程会查找其规范文件(JavaScript 或 TypeScript)以后缀 NativeComponent 结尾的组件。

JavaScript spec files must be named <FABRIC COMPONENT>NativeComponent.js (for TypeScript use extension .ts or .tsx) and they export a HostComponent object. The name convention is important: the Codegen process looks for components whose spec file (either JavaScript or TypeScript) ends with the suffix NativeComponent.

以下代码片段显示了一个基本的 JavaScript 规范模板,用 FlowTypeScript 编写。

The following snippet shows a basic JavaScript spec template, written in Flow as well as TypeScript.

// @flow strict-local

import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';

type NativeProps = $ReadOnly<{|
...ViewProps,
// add other props here
|}>;

export default (codegenNativeComponent<NativeProps>(
'<FABRIC COMPONENT>',
): HostComponent<NativeProps>);

支持的类型

当使用 Flow 或 TypeScript 时,你将使用 类型注释 来定义你的规范。 请记住,定义 JavaScript 规范的目标是确保生成的原生接口代码是类型安全的,受支持的类型集将是那些可以一对一映射到原生平台上相应类型的类型。

When using Flow or TypeScript, you will be using type annotations to define your spec. Keeping in mind that the goal of defining a JavaScript spec is to ensure the generated native interface code is type-safe, the set of supported types will be those that can be mapped one-to-one to a corresponding type on the native platform.

一般来说,这意味着你可以使用基本类型(字符串、数字、布尔值)、函数类型、对象类型和数组类型。 另一方面,不支持联合类型。 所有类型都必须是只读的。 对于流量: +$ReadOnly<>{||} 对象。 对于 TypeScript: readonly 表示属性,Readonly<> 表示对象,ReadonlyArray<> 表示数组。

In general, this means you can use primitive types (strings, numbers, booleans), function types, object types, and array types. Union types, on the other hand, are not supported. All types must be read-only. For Flow: either + or $ReadOnly<> or {||} objects. For TypeScript: readonly for properties, Readonly<> for objects, and ReadonlyArray<> for arrays.

参见附录 二. 流类型到原生类型的映射。 参见附录 三. TypeScript 到原生类型的映射

Codegen 辅助程序类型

你可以为 JavaScript 规范使用预定义类型,以下是它们的列表:

You can use predefined types for your JavaScript spec, here is a list of them:

  • Double
  • Float
  • Int32
  • UnsafeObject
  • WithDefault<Type, Value> - 设置类型的默认值
  • BubblingEventHandler<T> - 对于在组件树中从子级到父级向上传播(冒泡)直至根的事件(例如:onStartShouldSetResponder)。
  • DirectEventHandler<T> - 对于仅在接收事件的元素上调用的事件(例如:onClick)并且不冒泡。

随后,这些类型会被编译为目标平台上相应的等效类型。

Later on those types are compiled to coresponding equivalents on target platforms.

跨平台保持一致并消除类型歧义

在原生模块中采用新架构之前,你应该确保你的方法在各个平台上保持一致。 当你开始为原生模块编写 JavaScript 规范时,你将意识到这一点 - 请记住 JavaScript 规范定义了方法在所有受支持的平台上的外观。

Before adopting the New Architecture in your native module, you should ensure your methods are consistent across platforms. You will realize this as you set out to write the JavaScript spec for your native module - remember that JavaScript spec defines what the methods will look like on all supported platforms.

如果现有的原生模块在多个平台上具有相同名称的方法,但跨平台的参数数量或类型不同,则需要找到一种方法使这些方法保持一致。 如果你的方法可以为同一参数采用两种或多种不同类型,那么你需要找到一种方法来解决这种类型的歧义,因为故意不支持类型联合。

If your existing native module has methods with the same name on multiple platforms, but with different numbers or types of arguments across platforms, you will need to find a way to make these consistent. If you have methods that can take two or more different types for the same argument, then you need to find a way to resolve this type of ambiguity as type unions are intentionally not supported.

配置代码生成器

代码生成器 是一个在构建 Android 应用或安装 iOS 应用的依赖时运行的工具。 它会创建一些你无需手动创建的脚手架代码。

Codegen is a tool that runs when you build an Android app or install the dependencies of an iOS app. It creates some scaffolding code that you won't have to create manually.

可以在库的 package.json 文件中配置 Codegen。 在其末尾添加以下 JSON 对象。

Codegen can be configured in the package.json file of your Library. Add the following JSON object at the end of it.

  },
+ "codegenConfig": {
+ "name": "<library name>",
+ "type": "all",
+ "jsSrcsDir": ".",
+ "android": {
+ "javaPackageName": "com.facebook.fbreact.specs"
+ }
+ }
}
  • codegenConfig 是 Codegen 用于验证是否有代码要生成的密钥。
  • name 字段是库的名称。
  • type 字段用于标识我们要创建的模块的类型。 我们建议保留 all 以支持包含 Turbo Native Module 和 Fabric Native Components 的库。
  • jsSrcsDir 是代码生成器开始查找 JavaScript 规范的目录。
  • android.javaPackageName 是生成的代码最终所在的包的名称。

Android 还要求你的应用中包含 React Gradle 插件正确配置

Android also requires to have the React Gradle Plugin properly configured in your app.

UIManager JavaScript API 迁移

在新架构中,大多数 UIManager 方法将成为通过 ref 获取的原生组件实例上的实例方法:

In the New Architecture, most UIManager methods will become available as instance methods on native component instances obtained via ref:

function MyComponent(props: Props) {
const viewRef = useRef(null);

useEffect(() => {
viewRef.current.measure(((left, top, width, height, pageX, pageY) => {
// ...
});
}, []);

return <View ref={viewRef} />;
}

这种新的 API 设计具有以下几个优点:

This new API design provides several benefits:

  • 通过消除单独导入 UIManager 或调用 findNodeHandle 的需要,更好的开发者人机工程学。
  • 通过避免节点句柄查找步骤获得更好的性能。
  • findDOMNode 的类似弃用 方向对齐。

我们最终将弃用 UIManager。 然而,我们认识到,迁移对于许多应用和库作者来说需要高昂的成本。 为了最大限度地降低这一成本,我们计划在新架构中继续支持尽可能多的 UIManager 上的方法。

We will eventually deprecate UIManager. However, we recognize that migrations demand a high cost for many application and library authors. In order to minimize this cost, we plan to continue supporting as many of the methods on UIManager as possible in the New Architecture.

新架构中对 UIManager 方法的支持正在积极开发中。 虽然我们在这里取得了进展,但早期采用者仍然可以通过以下步骤来尝试新架构,以迁移常见的 UIManager API:

Support for UIManager methods in the New Architecture is actively being developed. While we make progress here, early adopters can still experiment with the New Architecture by following these steps to migrate off common UIManager APIs:

  1. 将对 requireNativeComponent 的调用移至单独的文件
  2. dispatchViewManagerCommand 迁移
  3. 使用 codegenNativeCommands 创建 NativeCommand

将对 requireNativeComponent 的调用移至单独的文件

这将为 JS 为新架构的新代码生成系统做好准备。 新文件应命名为 <ComponentName>NativeComponent.js.

This will prepare for the JS to be ready for the new codegen system for the New Architecture. The new file should be named <ComponentName>NativeComponent.js.

老办法

const RNTMyNativeView = requireNativeComponent('RNTMyNativeView');

[...]

return <RNTMyNativeView />;

新方法

RNTMyNativeNativeComponent.js
import RNTMyNativeViewNativeComponent from './RNTMyNativeViewNativeComponent';

[...]

return <RNTMyNativeViewNativeComponent />;
RNTMyNativeViewNativeComponent.js
import {requireNativeComponent} from 'react-native';

const RNTMyNativeViewNativeComponent = requireNativeComponent(
'RNTMyNativeView',
);

export default RNTMyNativeViewNativeComponent;

流量支持

如果没有输入 requireNativeComponent,可以暂时使用 mixed 类型来修复 Flow 警告,例如:

If requireNativeComponent is not typed, you can temporarily use the mixed type to fix the Flow warning, for example:

// @flow strict-local

import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
// ...
const RCTWebViewNativeComponent: HostComponent<mixed> =
requireNativeComponent<mixed>('RNTMyNativeView');

稍后你可以更换 requireNativeComponent

当你准备好迁移到 Fabric 时,可以将 requireNativeComponent 替换为 codegenNativeComponent

When you are ready to migrate to Fabric you can replace requireNativeComponent with codegenNativeComponent:

RNTMyNativeViewNativeComponent.js
// @flow strict-local

export default (codegenNativeComponent<NativeProps>(
'RNTMyNativeView',
): HostComponent<NativeProps>);

并更新主文件:

And update the main file:

RNTMyNativeNativeComponent.js
// @flow strict-local

export default require('./RNTMyNativeViewNativeComponent')
.default;

dispatchViewManagerCommand 迁移

与上面的类似,为了避免调用 UIManager 上的方法,所有视图管理器方法现在都通过 NativeCommands 的实例调用。 codegenNativeCommands 是一个新的 API,用于在给定视图管理器命令接口的情况下代码生成 NativeCommands

Similar to the one above, in an effort to avoid calling methods on the UIManager, all view manager methods are now called through an instance of NativeCommands. codegenNativeCommands is a new API to code-generate NativeCommands given an interface of your view manager’s commands.

之前

Before

class MyComponent extends React.Component<Props> {
_moveToRegion: (region: Region, duration: number) => {
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(this),
'moveToRegion',
[region, duration]
);
}

render() {
return <MyCustomMapNativeComponent onPress={this._moveToRegion} />
}
}

使用 codegenNativeCommands 创建 NativeCommand

Creating NativeCommands with codegenNativeCommands

MyCustomMapNativeComponent.js
// @flow strict-local

import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';

type MyCustomMapNativeComponentType = HostComponent<NativeProps>;

interface NativeCommands {
+moveToRegion: (
viewRef: React.ElementRef<MyCustomMapNativeComponentType>,
region: MapRegion,
duration: number,
) => void;
}

export const Commands: NativeCommands =
codegenNativeCommands<NativeCommands>({
supportedCommands: ['moveToRegion'],
});

注意:

Note:

  • moveToRegion 命令中的第一个参数是原生组件的 HostComponent 引用
  • moveToRegion 命令的参数在签名中枚举
  • 命令定义与原生组件位于同一位置。 这是一个值得鼓励的模式
  • 确保你已将命令名称包含在 supportedCommands 数组中

使用你的命令

// @flow strict-local

import {Commands, ...} from './MyCustomMapNativeComponent';

class MyComponent extends React.Component<Props> {
_ref: ?React.ElementRef<typeof MyCustomMapNativeComponent>;

_captureRef: (ref: React.ElementRef<typeof MyCustomMapNativeComponent>) => {
this._ref = ref;
}

_moveToRegion: (region: Region, duration: number) => {
if (this._ref != null) {
Commands.moveToRegion(this._ref, region, duration);
}
}

render() {
return <MyCustomMapNativeComponent
ref={this._captureRef}
onPress={this._moveToRegion} />
}
}

更新原生实现

在示例中,代码生成的 Commands 将向原生组件的视图管理器分派 moveToRegion 调用。 除了编写 JS 接口之外,你还需要更新原生实现签名以匹配分派的方法调用。 请参阅 Android 参数类型iOS 参数类型 的映射以供参考。

In the example, the code-generated Commands will dispatch moveToRegion call to the native component’s view manager. In addition to writing the JS interface, you’ll need to update your native implementation signatures to match the dispatched method call. See the mapping for Android argument types andiOS argument types for reference.

iOS

iOS

RCT_EXPORT_METHOD(moveToRegion:(nonnull NSNumber *)reactTag
region:(NSDictionary *)region
duration:(double)duration
{
...
}

安卓

Android

// receiveCommand signature has changed to receive String commandId
@Override
public void receiveCommand(
ReactMapDrawerView view, String commandId, @Nullable ReadableArray args) {
switch (commandId) {
case "moveToRegion":
if (args == null) {
break;
}

ReadableMap region = args.getMap(0);
int durationMs = args.getInt(1);
// ... act on the view...
break;
}
}