跳到主要内容
版本:0.78

使用 Codegen

本指南介绍如何:

  • 配置 Codegen
  • 为每个平台手动调用它。

它还描述了生成的代码。

前提条件

你始终需要一个 React Native 应用来正确生成代码,即使手动调用 Codegen 时也是如此。

Codegen 过程与应用的构建紧密耦合,脚本位于 react-native NPM 包中。

为了本指南的目的,使用 React Native CLI 创建一个项目,如下所示:

npx @react-native-community/cli@latest init SampleApp --version 0.76.0

Codegen 用于为你的自定义模块或组件生成胶水代码。有关如何创建它们的更多详细信息,请参阅 Turbo 原生模块和 Fabric 原生组件的指南。

配置 Codegen

可以通过修改 package.json 文件在你的应用中配置 CodegenCodegen 由一个名为 codegenConfig 的自定义字段控制。

package.json
  "codegenConfig": {
"name": "<SpecName>",
"type": "<types>",
"jsSrcsDir": "<source_dir>",
"android": {
"javaPackageName": "<java.package.name>"
},
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": [
"<iOS-class-conforming-to-RCTImageURLLoader>",
// 来自 react-native-camera-roll 的示例:https://github.com/react-native-cameraroll/react-native-cameraroll/blob/8a6d1b4279c76e5682a4b443e7a4e111e774ec0a/package.json#L118-L127
// "RNCPHAssetLoader",
],
"RCTURLRequestHandler": [
"<iOS-class-conforming-to-RCTURLRequestHandler>",
// 来自 react-native-camera-roll 的示例:https://github.com/react-native-cameraroll/react-native-cameraroll/blob/8a6d1b4279c76e5682a4b443e7a4e111e774ec0a/package.json#L118-L127
// "RNCPHAssetUploader",
],
"RCTImageDataDecoder": [
"<iOS-class-conforming-to-RCTImageDataDecoder>",
// 我们没有这方面的好的示例,但它的工作方式相同。传递实现 RCTImageDataDecoder 的类名。它必须是一个原生模块。
]
},
"componentProvider": {
"<componentName>": "<iOS-class-implementing-the-component>"
},
}
},

你可以将此代码片段添加到你的应用并自定义各个字段:

  • name: 这是用于创建包含你的规范的文件的名字。按照惯例,它应该有后缀 Spec,但这不是强制性的。
  • type: 我们需要生成的代码类型。允许的值为 modulescomponentsall
    • modules: 如果你只需要为 Turbo 原生模块生成代码,请使用此值。
    • components: 如果你只需要为原生 Fabric 组件生成代码,请使用此值。
    • all: 如果你混合了组件和模块,请使用此值。
  • jsSrcsDir: 这是所有规范所在的根文件夹。
  • android.javaPackageName: 这是 Android 特定的设置,让 Codegen 在自定义包中生成文件。
  • ios: ios 字段是一个对象,应用开发者和库维护者可以使用它来提供一些高级功能。以下所有字段都是可选的
    • ios.modulesConformingToProtocol: React Native 提供了一些原生模块可以实现以自定义某些行为的协议。这些字段允许你定义符合这些协议的模块列表。这些模块将在应用启动时注入到 React Native 运行时中。
      • ios.modulesConformingToProtocol.RCTImageURLLoader: 实现 RCTImageURLLoader 协议 的 iOS 原生模块列表。你需要传递实现 RCTImageURLLoader 的 iOS 类名。它们必须是原生模块。
      • ios.modulesConformingToProtocol.RCTURLRequestHandler: 实现 RCTURLRequestHandler 协议 的 iOS 原生模块列表。你需要传递实现 RCTURLRequestHandler 的 iOS 类名。它们必须是原生模块。
      • ios.modulesConformingToProtocol.RCTImageDataDecoder: 实现 RCTImageDataDecoder 协议 的 iOS 原生模块列表。你需要传递实现 RCTImageDataDecoder 的 iOS 类名。它们必须是原生模块。
    • ios.componentProvider: 此字段是一个映射,用于生成自定义 JS React 组件与实现它的原生类之间的关联。映射的键是组件的 JS 名称(例如 TextInput),值是实现该组件的 iOS 类(例如 RCTTextInput)。

Codegen 运行时,它会在应用的所有依赖项中搜索,寻找符合某些特定约定的 JS 文件,并生成所需的代码:

  • Turbo 原生模块要求规范文件以 Native 为前缀。例如,NativeLocalStorage.ts 是规范文件的有效名称。
  • 原生 Fabric 组件要求规范文件以 NativeComponent 为后缀。例如,WebViewNativeComponent.ts 是规范文件的有效名称。

运行 Codegen

本指南的其余部分假设你已经在项目中设置了原生 Turbo 模块、原生 Fabric 组件或两者。我们还假设你在 package.json 中指定的 jsSrcsDir 中有有效的规范文件。

Android

Android 的 Codegen 集成在 React Native Gradle Plugin (RNGP) 中。RNGP 包含一个可以调用的任务,该任务读取 package.json 文件中定义的配置并执行 Codegen。要运行 gradle 任务,首先导航到项目的 android 文件夹内。然后运行:

./gradlew generateCodegenArtifactsFromSchema

此任务在应用的所有导入项目(应用本身及其链接的所有 node 模块)上调用 generateCodegenArtifactsFromSchema 命令。它在相应的 node_modules/<dependency> 文件夹中生成代码。例如,如果你有一个 Fabric 原生组件,其 Node 模块称为 my-fabric-component,则生成的代码位于 SampleApp/node_modules/my-fabric-component/android/build/generated/source/codegen 路径。对于应用,代码生成在 android/app/build/generated/source/codegen 文件夹中。

生成的代码

运行上面的 gradle 命令后,你将在 SampleApp/android/app/build 文件夹中找到 codegen 代码。结构如下所示:

build
└── generated
└── source
└── codegen
├── java
│ └── com
│ ├── facebook
│ │ └── react
│ │ └── viewmanagers
│ │ ├── <nativeComponent>ManagerDelegate.java
│ │ └── <nativeComponent>ManagerInterface.java
│ └── sampleapp
│ └── NativeLocalStorageSpec.java
├── jni
│ ├── <codegenConfig.name>-generated.cpp
│ ├── <codegenConfig.name>.h
│ ├── CMakeLists.txt
│ └── react
│ └── renderer
│ └── components
│ └── <codegenConfig.name>
│ ├── <codegenConfig.name>JSI-generated.cpp
│ ├── <codegenConfig.name>.h
│ ├── ComponentDescriptors.cpp
│ ├── ComponentDescriptors.h
│ ├── EventEmitters.cpp
│ ├── EventEmitters.h
│ ├── Props.cpp
│ ├── Props.h
│ ├── ShadowNodes.cpp
│ ├── ShadowNodes.h
│ ├── States.cpp
│ └── States.h
└── schema.json

生成的代码分为两个文件夹:

  • java 包含平台特定的代码
  • jni 包含让 JS 和 Java 正确交互所需的 C++ 代码。

java 文件夹中,你可以在 com/facebook/viewmanagers 子文件夹中找到 Fabric 原生组件生成的代码。

  • <nativeComponent>ManagerDelegate.java 包含 ViewManager 可以在自定义原生组件上调用的方法
  • <nativeComponent>ManagerInterface.java 包含 ViewManager 的接口。

codegenConfig.android.javaPackageName 中设置的文件夹中,你可以找到 Turbo 原生模块必须实现以执行其任务的抽象类。

最后在 jni 文件夹中,有所有将 JS 连接到 Android 的样板代码。

  • <codegenConfig.name>.h 这包含你的自定义 C++ Turbo 原生模块的接口。
  • <codegenConfig.name>-generated.cpp 这包含你的自定义 C++ Turbo 原生模块的胶水代码。
  • react/renderer/components/<codegenConfig.name>: 此文件夹包含你的自定义组件所需的所有胶水代码。

此结构是使用 codegenConfig.type 字段的值 all 生成的。如果你使用值 modules,期望看不到 react/renderer/components/ 文件夹。如果你使用值 components,期望看不到任何其他文件。

iOS

iOS 的 Codegen 依赖于一些在构建过程中调用的 Node 脚本。脚本位于 SampleApp/node_modules/react-native/scripts/ 文件夹中。

主脚本是 generate-codegen-artifacts.js 脚本。要调用脚本,你可以从应用的根文件夹运行此命令:

node node_modules/react-native/scripts/generate-codegen-artifacts.js

用法:generate-codegen-artifacts.js -p [path to app] -t [target platform] -o [output path]

选项:
--help 显示帮助 [boolean]
--version 显示版本号 [boolean]
-p, --path React Native 项目根目录的路径。 [required]
-t, --targetPlatform 目标平台。支持的值:"android", "ios",
"all". [required]
-o, --outputPath 生成产物输出的路径。

其中:

  • --path 是你的应用根文件夹的路径。
  • --outputPathCodegen 将写入生成文件的目标位置。
  • --targetPlatform 是你想要为其生成代码的平台。

生成的代码

使用这些参数运行脚本:

node node_modules/react-native/scripts/generate-codegen-artifacts.js \
--path . \
--outputPath ios/ \
--targetPlatform ios

将在 ios/build 文件夹中生成这些文件:

build
└── generated
└── ios
├── <codegenConfig.name>
│ ├── <codegenConfig.name>-generated.mm
│ └── <codegenConfig.name>.h
├── <codegenConfig.name>JSI-generated.cpp
├── <codegenConfig.name>JSI.h
├── FBReactNativeSpec
│ ├── FBReactNativeSpec-generated.mm
│ └── FBReactNativeSpec.h
├── FBReactNativeSpecJSI-generated.cpp
├── FBReactNativeSpecJSI.h
├── RCTModulesConformingToProtocolsProvider.h
├── RCTModulesConformingToProtocolsProvider.mm
└── react
└── renderer
└── components
└── <codegenConfig.name>
├── ComponentDescriptors.cpp
├── ComponentDescriptors.h
├── EventEmitters.cpp
├── EventEmitters.h
├── Props.cpp
├── Props.h
├── RCTComponentViewHelpers.h
├── ShadowNodes.cpp
├── ShadowNodes.h
├── States.cpp
└── States.h

部分生成的文件被 React Native 在 Core 中使用。然后有一组文件包含你在 package.json codegenConfig.name 字段中指定的相同名称。

  • <codegenConfig.name>/<codegenConfig.name>.h: 这包含你的自定义 iOS Turbo 原生模块的接口。
  • <codegenConfig.name>/<codegenConfig.name>-generated.mm: 这包含你的自定义 iOS Turbo 原生模块的胶水代码。
  • <codegenConfig.name>JSI.h: 这包含你的自定义 C++ Turbo 原生模块的接口。
  • <codegenConfig.name>JSI-generated.h: 这包含你的自定义 C++ Turbo 原生模块的胶水代码。
  • react/renderer/components/<codegenConfig.name>: 此文件夹包含你的自定义组件所需的所有胶水代码。

此结构是使用 codegenConfig.type 字段的值 all 生成的。如果你使用值 modules,期望看不到 react/renderer/components/ 文件夹。如果你使用值 components,期望看不到任何其他文件。