使用 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 Native Modules(Turbo 原生模块)和 Fabric Native Components(Fabric 原生组件)的指南。
配置 Codegen
可以通过修改你的应用的 package.json
文件来配置 Codegen。Codegen 由名为 codegenConfig
的自定义字段控制。
"codegenConfig": {
"name": "<SpecName>",
"type": "<types>",
"jsSrcsDir": "<source_dir>",
"android": {
"javaPackageName": "<java.package.name>"
},
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": [
"<iOS-class-conforming-to-RCTImageURLLoader>",
// example from 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>",
// example from 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>",
// we don't have a good example for this, but it works in the same way. Pass the name of the class that implements the RCTImageDataDecoder. It must be a Native Module.
]
},
"componentProvider": {
"<componentName>": "<iOS-class-implementing-the-component>"
},
}
},
你可以将此代码段添加到你的应用并自定义各种字段:
name:
这是用于创建包含你的规范文件的名称。按照惯例,它应该带有后缀Spec
,但这并非强制性的。type
: 我们需要生成的代码类型。允许的值为modules
、components
、all
。modules:
如果你只需要为 Turbo Native Modules(Turbo 原生模块)生成代码,请使用此值。components:
如果你只需要为 Native Fabric 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 Modules(Turbo 原生模块)要求规范文件以
Native
为前缀。例如,NativeLocalStorage.ts
是规范文件的有效名称。 - Native Fabric Components(原生 Fabric 组件)要求规范文件以
NativeComponent
为后缀。例如,WebViewNativeComponent.ts
是规范文件的有效名称。
运行 Codegen
本指南的其余部分假设你的项目中已经设置了 Native Turbo Module(原生 Turbo 模块)、Native Fabric Component(原生 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>
文件夹中生成代码。例如,如果你有一个 Node 模块名为 my-fabric-component
的 Fabric Native Component(Fabric 原生组件),则生成的代码位于 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 Native component(Fabric 原生组件)代码。
<nativeComponent>ManagerDelegate.java
包含ViewManager
可以在自定义 Native Component(原生组件)上调用的方法。<nativeComponent>ManagerInterface.java
包含ViewManager
的接口。
在名称在 codegenConfig.android.javaPackageName
中设置的文件夹中,你可以找到 Turbo Native Module(Turbo 原生模块)必须实现的抽象类,以执行其任务。
最后,在 jni
文件夹中,有所有将 JS 连接到 Android 的样板代码。
<codegenConfig.name>.h
:这包含你的自定义 C++ Turbo Native Modules(Turbo 原生模块)的接口。<codegenConfig.name>-generated.cpp
:这包含你的自定义 C++ Turbo Native Modules(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
Usage: generate-codegen-artifacts.js -p [path to app] -t [target platform] -o [output path]
Options:
--help Show help [boolean]
--version Show version number [boolean]
-p, --path Path to the React Native project root. [required]
-t, --targetPlatform Target platform. Supported values: "android", "ios",
"all". [required]
-o, --outputPath Path where generated artifacts will be output to.
其中:
--path
是你的应用的根文件夹的路径。--outputPath
是 Codegen 将写入生成文件的目标位置。--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 Native Modules(Turbo 原生模块)的接口。<codegenConfig.name>/<codegenConfig.name>-generated.mm
:这包含你的自定义 iOS Turbo Native Modules(Turbo 原生模块)的粘合代码。<codegenConfig.name>JSI.h
:这包含你的自定义 C++ Turbo Native Modules(Turbo 原生模块)的接口。<codegenConfig.name>JSI-generated.h
:这包含你的自定义 C++ Turbo Native Modules(Turbo 原生模块)的粘合代码。react/renderer/components/<codegenConfig.name>
: 此文件夹包含你的自定义组件所需的所有粘合代码。
此结构是通过为 codegenConfig.type
字段使用值 all
生成的。如果你使用值 modules
,则预计看不到 react/renderer/components/
文件夹。如果你使用值 components
,则预计看不到任何其他文件。